xref: /reactos/drivers/hid/mouhid/mouhid.c (revision 6717a4ec)
1 /*
2  * PROJECT:     ReactOS HID Stack
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        drivers/hid/mouhid/mouhid.c
5  * PURPOSE:     Mouse HID Driver
6  * PROGRAMMERS:
7  *              Michael Martin (michael.martin@reactos.org)
8  *              Johannes Anderwald (johannes.anderwald@reactos.org)
9  */
10 
11 #include "mouhid.h"
12 
13 static USHORT MouHid_ButtonUpFlags[] =
14 {
15     MOUSE_LEFT_BUTTON_DOWN,
16     MOUSE_RIGHT_BUTTON_DOWN,
17     MOUSE_MIDDLE_BUTTON_DOWN,
18     MOUSE_BUTTON_4_DOWN,
19     MOUSE_BUTTON_5_DOWN
20 };
21 
22 static USHORT MouHid_ButtonDownFlags[] =
23 {
24     MOUSE_LEFT_BUTTON_UP,
25     MOUSE_RIGHT_BUTTON_UP,
26     MOUSE_MIDDLE_BUTTON_UP,
27     MOUSE_BUTTON_4_UP,
28     MOUSE_BUTTON_5_UP
29 };
30 
31 VOID
32 MouHid_GetButtonMove(
33     IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
34     OUT PLONG LastX,
35     OUT PLONG LastY)
36 {
37     NTSTATUS Status;
38 
39     /* init result */
40     *LastX = 0;
41     *LastY = 0;
42 
43     /* get scaled usage value x */
44     Status =  HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_X, (PLONG)LastX, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength);
45     /* FIXME handle error */
46     ASSERT(Status == HIDP_STATUS_SUCCESS);
47 
48     /* get scaled usage value y */
49     Status =  HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_Y, (PLONG)LastY, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength);
50     /* FIXME handle error */
51     ASSERT(Status == HIDP_STATUS_SUCCESS);
52 
53 }
54 
55 
56 VOID
57 MouHid_GetButtonFlags(
58     IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
59     OUT PUSHORT ButtonFlags)
60 {
61     NTSTATUS Status;
62     USAGE Usage;
63     ULONG Index;
64     PUSAGE TempList;
65     ULONG CurrentUsageListLength;
66 
67     /* init flags */
68     *ButtonFlags = 0;
69 
70     /* get usages */
71     CurrentUsageListLength = DeviceExtension->UsageListLength;
72     Status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, HIDP_LINK_COLLECTION_UNSPECIFIED, DeviceExtension->CurrentUsageList, &CurrentUsageListLength, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength);
73     if (Status != HIDP_STATUS_SUCCESS)
74     {
75         DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status);
76         return;
77     }
78 
79     /* extract usage list difference */
80     Status = HidP_UsageListDifference(DeviceExtension->PreviousUsageList, DeviceExtension->CurrentUsageList, DeviceExtension->BreakUsageList, DeviceExtension->MakeUsageList, DeviceExtension->UsageListLength);
81     if (Status != HIDP_STATUS_SUCCESS)
82     {
83         DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status);
84         return;
85     }
86 
87     if (DeviceExtension->UsageListLength)
88     {
89         Index = 0;
90         do
91         {
92             /* get usage */
93             Usage = DeviceExtension->BreakUsageList[Index];
94             if (!Usage)
95                 break;
96 
97             if (Usage <= 5)
98             {
99                 /* max 5 buttons supported */
100                 *ButtonFlags |= MouHid_ButtonDownFlags[Usage];
101             }
102 
103             /* move to next index*/
104             Index++;
105         }while(Index < DeviceExtension->UsageListLength);
106     }
107 
108     if (DeviceExtension->UsageListLength)
109     {
110         Index = 0;
111         do
112         {
113             /* get usage */
114             Usage = DeviceExtension->MakeUsageList[Index];
115             if (!Usage)
116                 break;
117 
118             if (Usage <= 5)
119             {
120                 /* max 5 buttons supported */
121                 *ButtonFlags |= MouHid_ButtonUpFlags[Usage];
122             }
123 
124             /* move to next index*/
125             Index++;
126         }while(Index < DeviceExtension->UsageListLength);
127     }
128 
129     /* now switch the previous list with current list */
130     TempList = DeviceExtension->CurrentUsageList;
131     DeviceExtension->CurrentUsageList = DeviceExtension->PreviousUsageList;
132     DeviceExtension->PreviousUsageList = TempList;
133 }
134 
135 VOID
136 MouHid_DispatchInputData(
137     IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
138     IN PMOUSE_INPUT_DATA InputData)
139 {
140     KIRQL OldIrql;
141     ULONG InputDataConsumed;
142 
143     if (!DeviceExtension->ClassService)
144         return;
145 
146     /* sanity check */
147     ASSERT(DeviceExtension->ClassService);
148     ASSERT(DeviceExtension->ClassDeviceObject);
149 
150     /* raise irql */
151     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
152 
153     /* dispatch input data */
154     (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(DeviceExtension->ClassDeviceObject, InputData, InputData + 1, &InputDataConsumed);
155 
156     /* lower irql to previous level */
157     KeLowerIrql(OldIrql);
158 }
159 
160 NTSTATUS
161 NTAPI
162 MouHid_ReadCompletion(
163     IN PDEVICE_OBJECT  DeviceObject,
164     IN PIRP  Irp,
165     IN PVOID  Context)
166 {
167     PMOUHID_DEVICE_EXTENSION DeviceExtension;
168     USHORT ButtonFlags;
169     LONG UsageValue;
170     NTSTATUS Status;
171     LONG LastX, LastY;
172     MOUSE_INPUT_DATA MouseInputData;
173 
174     /* get device extension */
175     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)Context;
176 
177     if (Irp->IoStatus.Status == STATUS_PRIVILEGE_NOT_HELD ||
178         Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED ||
179         Irp->IoStatus.Status == STATUS_CANCELLED ||
180         DeviceExtension->StopReadReport)
181     {
182         /* failed to read or should be stopped*/
183         DPRINT1("[MOUHID] ReadCompletion terminating read Status %x\n", Irp->IoStatus.Status);
184 
185         /* report no longer active */
186         DeviceExtension->ReadReportActive = FALSE;
187 
188         /* request stopping of the report cycle */
189         DeviceExtension->StopReadReport = FALSE;
190 
191         /* signal completion event */
192         KeSetEvent(&DeviceExtension->ReadCompletionEvent, 0, 0);
193         return STATUS_MORE_PROCESSING_REQUIRED;
194     }
195 
196     /* get mouse change flags */
197     MouHid_GetButtonFlags(DeviceExtension, &ButtonFlags);
198 
199     /* get mouse change */
200     MouHid_GetButtonMove(DeviceExtension, &LastX, &LastY);
201 
202     /* init input data */
203     RtlZeroMemory(&MouseInputData, sizeof(MOUSE_INPUT_DATA));
204 
205     /* init input data */
206     MouseInputData.ButtonFlags = ButtonFlags;
207     MouseInputData.LastX = LastX;
208     MouseInputData.LastY = LastY;
209 
210     /* detect mouse wheel change */
211     if (DeviceExtension->MouseIdentifier == WHEELMOUSE_HID_HARDWARE)
212     {
213         /* get usage */
214         UsageValue = 0;
215         Status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_WHEEL, &UsageValue, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength);
216         if (Status == HIDP_STATUS_SUCCESS)
217         {
218             /* store wheel status */
219             MouseInputData.ButtonFlags |= MOUSE_WHEEL;
220             MouseInputData.ButtonData = (USHORT)UsageValue; /* FIXME */
221         }
222         else
223         {
224             DPRINT1("[MOUHID] failed to get wheel status with %x\n", Status);
225         }
226     }
227 
228     DPRINT1("[MOUHID] LastX %ld LastY %ld Flags %x ButtonData %x\n", MouseInputData.LastX, MouseInputData.LastY, MouseInputData.ButtonFlags, MouseInputData.ButtonData);
229 
230     /* dispatch mouse action */
231     MouHid_DispatchInputData(DeviceExtension, &MouseInputData);
232 
233     /* re-init read */
234     MouHid_InitiateRead(DeviceExtension);
235 
236     /* stop completion */
237     return STATUS_MORE_PROCESSING_REQUIRED;
238 }
239 
240 NTSTATUS
241 MouHid_InitiateRead(
242     IN PMOUHID_DEVICE_EXTENSION DeviceExtension)
243 {
244     PIO_STACK_LOCATION IoStack;
245     NTSTATUS Status;
246 
247     /* re-use irp */
248     IoReuseIrp(DeviceExtension->Irp, STATUS_SUCCESS);
249 
250     /* init irp */
251     DeviceExtension->Irp->MdlAddress = DeviceExtension->ReportMDL;
252 
253     /* get next stack location */
254     IoStack = IoGetNextIrpStackLocation(DeviceExtension->Irp);
255 
256     /* init stack location */
257     IoStack->Parameters.Read.Length = DeviceExtension->ReportLength;
258     IoStack->Parameters.Read.Key = 0;
259     IoStack->Parameters.Read.ByteOffset.QuadPart = 0LL;
260     IoStack->MajorFunction = IRP_MJ_READ;
261     IoStack->FileObject = DeviceExtension->FileObject;
262 
263     /* set completion routine */
264     IoSetCompletionRoutine(DeviceExtension->Irp, MouHid_ReadCompletion, DeviceExtension, TRUE, TRUE, TRUE);
265 
266     /* read is active */
267     DeviceExtension->ReadReportActive = TRUE;
268 
269     /* start the read */
270     Status = IoCallDriver(DeviceExtension->NextDeviceObject, DeviceExtension->Irp);
271 
272     /* done */
273     return Status;
274 }
275 
276 NTSTATUS
277 NTAPI
278 MouHid_CreateCompletion(
279     IN PDEVICE_OBJECT  DeviceObject,
280     IN PIRP  Irp,
281     IN PVOID  Context)
282 {
283     KeSetEvent((PKEVENT)Context, 0, FALSE);
284     return STATUS_MORE_PROCESSING_REQUIRED;
285 }
286 
287 
288 NTSTATUS
289 NTAPI
290 MouHid_Create(
291     IN PDEVICE_OBJECT DeviceObject,
292     IN PIRP Irp)
293 {
294     PIO_STACK_LOCATION IoStack;
295     NTSTATUS Status;
296     KEVENT Event;
297     PMOUHID_DEVICE_EXTENSION DeviceExtension;
298 
299     DPRINT1("MOUHID: IRP_MJ_CREATE\n");
300 
301     /* get device extension */
302     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
303 
304     /* get stack location */
305     IoStack = IoGetCurrentIrpStackLocation(Irp);
306 
307     /* copy stack location to next */
308     IoCopyCurrentIrpStackLocationToNext(Irp);
309 
310     /* init event */
311     KeInitializeEvent(&Event, NotificationEvent, FALSE);
312 
313     /* prepare irp */
314     IoSetCompletionRoutine(Irp, MouHid_CreateCompletion, &Event, TRUE, TRUE, TRUE);
315 
316     /* call lower driver */
317     Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
318     if (Status == STATUS_PENDING)
319     {
320         /* request pending */
321         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
322     }
323 
324     /* check for success */
325     if (!NT_SUCCESS(Status))
326     {
327         /* failed */
328         Irp->IoStatus.Status = Status;
329         IoCompleteRequest(Irp, IO_NO_INCREMENT);
330         return Status;
331     }
332 
333     /* is the driver already in use */
334     if (DeviceExtension->FileObject == NULL)
335     {
336          /* did the caller specify correct attributes */
337          ASSERT(IoStack->Parameters.Create.SecurityContext);
338          if (IoStack->Parameters.Create.SecurityContext->DesiredAccess)
339          {
340              /* store file object */
341              DeviceExtension->FileObject = IoStack->FileObject;
342 
343              /* reset event */
344              KeResetEvent(&DeviceExtension->ReadCompletionEvent);
345 
346              /* initiating read */
347              Status = MouHid_InitiateRead(DeviceExtension);
348              DPRINT1("[MOUHID] MouHid_InitiateRead: status %x\n", Status);
349              if (Status == STATUS_PENDING)
350              {
351                  /* report irp is pending */
352                  Status = STATUS_SUCCESS;
353              }
354          }
355     }
356 
357     /* complete request */
358     Irp->IoStatus.Status = Status;
359     IoCompleteRequest(Irp, IO_NO_INCREMENT);
360     return Status;
361 }
362 
363 
364 NTSTATUS
365 NTAPI
366 MouHid_Close(
367     IN PDEVICE_OBJECT DeviceObject,
368     IN PIRP Irp)
369 {
370     PMOUHID_DEVICE_EXTENSION DeviceExtension;
371 
372     /* get device extension */
373     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
374 
375     DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive);
376 
377     if (DeviceExtension->ReadReportActive)
378     {
379         /* request stopping of the report cycle */
380         DeviceExtension->StopReadReport = TRUE;
381 
382         /* wait until the reports have been read */
383         KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL);
384 
385         /* cancel irp */
386         IoCancelIrp(DeviceExtension->Irp);
387     }
388 
389     DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive);
390 
391     /* remove file object */
392     DeviceExtension->FileObject = NULL;
393 
394     /* skip location */
395     IoSkipCurrentIrpStackLocation(Irp);
396 
397     /* pass irp to down the stack */
398     return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
399 }
400 
401 NTSTATUS
402 NTAPI
403 MouHid_DeviceControl(
404     IN PDEVICE_OBJECT DeviceObject,
405     IN PIRP Irp)
406 {
407     PIO_STACK_LOCATION IoStack;
408     PMOUSE_ATTRIBUTES Attributes;
409     PMOUHID_DEVICE_EXTENSION DeviceExtension;
410     PCONNECT_DATA Data;
411 
412     /* get current stack location */
413     IoStack = IoGetCurrentIrpStackLocation(Irp);
414 
415     DPRINT1("[MOUHID] DeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
416 
417     /* get device extension */
418     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
419 
420     /* handle requests */
421     if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_MOUSE_QUERY_ATTRIBUTES)
422     {
423          /* verify output buffer length */
424          if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES))
425          {
426              /* invalid request */
427              Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
428              IoCompleteRequest(Irp, IO_NO_INCREMENT);
429              return STATUS_BUFFER_TOO_SMALL;
430          }
431 
432          /* get output buffer */
433          Attributes = (PMOUSE_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer;
434 
435          /* type of mouse */
436          Attributes->MouseIdentifier = DeviceExtension->MouseIdentifier;
437 
438          /* number of buttons */
439          Attributes->NumberOfButtons = DeviceExtension->UsageListLength;
440 
441          /* sample rate not used for usb */
442          Attributes->SampleRate = 0;
443 
444          /* queue length */
445          Attributes->InputDataQueueLength = 2;
446 
447          /* complete request */
448          Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
449          Irp->IoStatus.Status = STATUS_SUCCESS;
450          IoCompleteRequest(Irp, IO_NO_INCREMENT);
451          return STATUS_SUCCESS;
452     }
453     else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_CONNECT)
454     {
455          /* verify input buffer length */
456          if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA))
457          {
458              /* invalid request */
459              Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
460              IoCompleteRequest(Irp, IO_NO_INCREMENT);
461              return STATUS_INVALID_PARAMETER;
462          }
463 
464          /* is it already connected */
465          if (DeviceExtension->ClassService)
466          {
467              /* already connected */
468              Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
469              IoCompleteRequest(Irp, IO_NO_INCREMENT);
470              return STATUS_SHARING_VIOLATION;
471          }
472 
473          /* get connect data */
474          Data = (PCONNECT_DATA)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
475 
476          /* store connect details */
477          DeviceExtension->ClassDeviceObject = Data->ClassDeviceObject;
478          DeviceExtension->ClassService = Data->ClassService;
479 
480          /* completed successfully */
481          Irp->IoStatus.Status = STATUS_SUCCESS;
482          IoCompleteRequest(Irp, IO_NO_INCREMENT);
483          return STATUS_SUCCESS;
484     }
485     else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_DISCONNECT)
486     {
487         /* not supported */
488         Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
489         IoCompleteRequest(Irp, IO_NO_INCREMENT);
490         return STATUS_NOT_IMPLEMENTED;
491     }
492     else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_ENABLE)
493     {
494         /* not supported */
495         Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
496         IoCompleteRequest(Irp, IO_NO_INCREMENT);
497         return STATUS_NOT_SUPPORTED;
498     }
499     else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_DISABLE)
500     {
501         /* not supported */
502         Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
503         IoCompleteRequest(Irp, IO_NO_INCREMENT);
504         return STATUS_INVALID_DEVICE_REQUEST;
505     }
506 
507     /* unknown request not supported */
508     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
509     IoCompleteRequest(Irp, IO_NO_INCREMENT);
510     return STATUS_NOT_SUPPORTED;
511 }
512 
513 NTSTATUS
514 NTAPI
515 MouHid_InternalDeviceControl(
516     IN PDEVICE_OBJECT DeviceObject,
517     IN PIRP Irp)
518 {
519     PMOUHID_DEVICE_EXTENSION DeviceExtension;
520 
521     /* get device extension */
522     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
523 
524     /* skip stack location */
525     IoSkipCurrentIrpStackLocation(Irp);
526 
527     /* pass and forget */
528     return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
529 }
530 
531 NTSTATUS
532 NTAPI
533 MouHid_Power(
534     IN PDEVICE_OBJECT DeviceObject,
535     IN PIRP Irp)
536 {
537     UNIMPLEMENTED
538     ASSERT(FALSE);
539     return STATUS_NOT_IMPLEMENTED;
540 }
541 
542 NTSTATUS
543 MouHid_SubmitRequest(
544     PDEVICE_OBJECT DeviceObject,
545     ULONG IoControlCode,
546     ULONG InputBufferSize,
547     PVOID InputBuffer,
548     ULONG OutputBufferSize,
549     PVOID OutputBuffer)
550 {
551     KEVENT Event;
552     PMOUHID_DEVICE_EXTENSION DeviceExtension;
553     PIRP Irp;
554     NTSTATUS Status;
555     IO_STATUS_BLOCK IoStatus;
556 
557     /* get device extension */
558     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
559 
560     /* init event */
561     KeInitializeEvent(&Event, NotificationEvent, FALSE);
562 
563     /* build request */
564     Irp = IoBuildDeviceIoControlRequest(IoControlCode, DeviceExtension->NextDeviceObject, InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize, FALSE, &Event, &IoStatus);
565     if (!Irp)
566     {
567         /* no memory */
568         return STATUS_INSUFFICIENT_RESOURCES;
569     }
570 
571     /* send request */
572     Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
573     if (Status == STATUS_PENDING)
574     {
575         /* wait for request to complete */
576         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
577         Status = IoStatus.Status;
578     }
579 
580     /* done */
581     return Status;
582 }
583 
584 NTSTATUS
585 NTAPI
586 MouHid_StartDevice(
587     IN PDEVICE_OBJECT DeviceObject)
588 {
589     NTSTATUS Status;
590     ULONG Buttons;
591     HID_COLLECTION_INFORMATION Information;
592     PVOID PreparsedData;
593     HIDP_CAPS Capabilities;
594     ULONG ValueCapsLength;
595     HIDP_VALUE_CAPS ValueCaps;
596     PMOUHID_DEVICE_EXTENSION DeviceExtension;
597     PUSHORT Buffer;
598 
599     /* get device extension */
600     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
601 
602     /* query collection information */
603     Status = MouHid_SubmitRequest(DeviceObject, IOCTL_HID_GET_COLLECTION_INFORMATION, 0, NULL, sizeof(HID_COLLECTION_INFORMATION), &Information);
604     if (!NT_SUCCESS(Status))
605     {
606         /* failed to query collection information */
607         DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status);
608         return Status;
609     }
610 
611     /* lets allocate space for preparsed data */
612     PreparsedData = ExAllocatePool(NonPagedPool, Information.DescriptorSize);
613     if (!PreparsedData)
614     {
615         /* no memory */
616         DPRINT1("[MOUHID] no memory size %u\n", Information.DescriptorSize);
617         return STATUS_INSUFFICIENT_RESOURCES;
618     }
619 
620     /* now obtain the preparsed data */
621     Status = MouHid_SubmitRequest(DeviceObject, IOCTL_HID_GET_COLLECTION_DESCRIPTOR, 0, NULL, Information.DescriptorSize, PreparsedData);
622     if (!NT_SUCCESS(Status))
623     {
624         /* failed to get preparsed data */
625         DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status);
626         ExFreePool(PreparsedData);
627         return Status;
628     }
629 
630     /* lets get the caps */
631     Status = HidP_GetCaps(PreparsedData, &Capabilities);
632     if (!NT_SUCCESS(Status))
633     {
634         /* failed to get capabilities */
635         DPRINT1("[MOUHID] failed to obtain caps with %x\n", Status);
636         ExFreePool(PreparsedData);
637         return Status;
638     }
639 
640     DPRINT1("[MOUHID] Usage %x UsagePage %x\n", Capabilities.Usage, Capabilities.UsagePage, Capabilities.InputReportByteLength);
641 
642     /* verify capabilities */
643     if (Capabilities.Usage != HID_USAGE_GENERIC_POINTER && Capabilities.Usage != HID_USAGE_GENERIC_MOUSE || Capabilities.UsagePage != HID_USAGE_PAGE_GENERIC)
644     {
645         /* not supported */
646         ExFreePool(PreparsedData);
647         return STATUS_UNSUCCESSFUL;
648     }
649 
650     /* init input report*/
651     DeviceExtension->ReportLength = Capabilities.InputReportByteLength;
652     ASSERT(DeviceExtension->ReportLength);
653     DeviceExtension->Report = (PUCHAR)ExAllocatePool(NonPagedPool, DeviceExtension->ReportLength);
654     ASSERT(DeviceExtension->Report);
655     RtlZeroMemory(DeviceExtension->Report, DeviceExtension->ReportLength);
656 
657     /* build mdl */
658     DeviceExtension->ReportMDL = IoAllocateMdl(DeviceExtension->Report, DeviceExtension->ReportLength, FALSE, FALSE, NULL);
659     ASSERT(DeviceExtension->ReportMDL);
660 
661     /* init mdl */
662     MmBuildMdlForNonPagedPool(DeviceExtension->ReportMDL);
663 
664     /* get max number of buttons */
665     Buttons = HidP_MaxUsageListLength(HidP_Input, HID_USAGE_PAGE_BUTTON, PreparsedData);
666     DPRINT1("[MOUHID] Buttons %lu\n", Buttons);
667     ASSERT(Buttons > 0);
668 
669     /* now allocate an array for those buttons */
670     Buffer = ExAllocatePool(NonPagedPool, sizeof(USAGE) * 4 * Buttons);
671     if (!Buffer)
672     {
673         /* no memory */
674         ExFreePool(PreparsedData);
675         return STATUS_INSUFFICIENT_RESOURCES;
676     }
677 
678     /* init usage lists */
679     RtlZeroMemory(Buffer, sizeof(USAGE) * 4 * Buttons);
680     DeviceExtension->CurrentUsageList = Buffer;
681     Buffer += Buttons;
682     DeviceExtension->PreviousUsageList = Buffer;
683     Buffer += Buttons;
684     DeviceExtension->MakeUsageList = Buffer;
685     Buffer += Buttons;
686     DeviceExtension->BreakUsageList = Buffer;
687 
688     /* store number of buttons */
689     DeviceExtension->UsageListLength = (USHORT)Buttons;
690 
691     /* store preparsed data */
692     DeviceExtension->PreparsedData = PreparsedData;
693 
694     ValueCapsLength = 1;
695     HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_X, &ValueCaps, &ValueCapsLength, PreparsedData);
696 
697     ValueCapsLength = 1;
698     HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_Y, &ValueCaps, &ValueCapsLength, PreparsedData);
699 
700     /* now check for wheel mouse support */
701     ValueCapsLength = 1;
702     Status = HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_WHEEL, &ValueCaps, &ValueCapsLength, PreparsedData);
703     if (Status == HIDP_STATUS_SUCCESS )
704     {
705         /* mouse has wheel support */
706         DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
707         DeviceExtension->WheelUsagePage = ValueCaps.UsagePage;
708         DPRINT1("[MOUHID] mouse wheel support detected\n", Status);
709     }
710     else
711     {
712         /* check if the mouse has z-axis */
713         ValueCapsLength = 1;
714         Status = HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_Z, &ValueCaps, &ValueCapsLength, PreparsedData);
715         if (Status == HIDP_STATUS_SUCCESS && ValueCapsLength == 1)
716         {
717             /* wheel support */
718             DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
719             DeviceExtension->WheelUsagePage = ValueCaps.UsagePage;
720             DPRINT1("[MOUHID] mouse wheel support detected with z-axis\n", Status);
721         }
722     }
723 
724     /* completed successfully */
725     return STATUS_SUCCESS;
726 }
727 
728 NTSTATUS
729 NTAPI
730 MouHid_StartDeviceCompletion(
731     IN PDEVICE_OBJECT  DeviceObject,
732     IN PIRP  Irp,
733     IN PVOID  Context)
734 {
735     KeSetEvent((PKEVENT)Context, 0, FALSE);
736     return STATUS_MORE_PROCESSING_REQUIRED;
737 }
738 
739 NTSTATUS
740 NTAPI
741 MouHid_Pnp(
742     IN PDEVICE_OBJECT DeviceObject,
743     IN PIRP Irp)
744 {
745     PIO_STACK_LOCATION IoStack;
746     KEVENT Event;
747     NTSTATUS Status;
748     PMOUHID_DEVICE_EXTENSION DeviceExtension;
749 
750     /* get device extension */
751     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
752 
753     /* get current irp stack */
754     IoStack = IoGetCurrentIrpStackLocation(Irp);
755     DPRINT1("[MOUHID] IRP_MJ_PNP Request: %x\n", IoStack->MinorFunction);
756 
757     if (IoStack->MinorFunction == IRP_MN_STOP_DEVICE || IoStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE || IoStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE || IoStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE)
758     {
759         /* indicate success */
760         Irp->IoStatus.Status = STATUS_SUCCESS;
761 
762         /* skip irp stack location */
763         IoSkipCurrentIrpStackLocation(Irp);
764 
765         /* dispatch to lower device */
766         return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
767     }
768     else if (IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE)
769     {
770         /* FIXME synchronization */
771 
772         /* cancel irp */
773         IoCancelIrp(DeviceExtension->Irp);
774 
775         /* indicate success */
776         Irp->IoStatus.Status = STATUS_SUCCESS;
777 
778         /* skip irp stack location */
779         IoSkipCurrentIrpStackLocation(Irp);
780 
781         /* dispatch to lower device */
782         Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
783 
784         IoFreeIrp(DeviceExtension->Irp);
785         IoDetachDevice(DeviceExtension->NextDeviceObject);
786         IoDeleteDevice(DeviceObject);
787         return Status;
788     }
789     else if (IoStack->MinorFunction == IRP_MN_START_DEVICE)
790     {
791         /* init event */
792         KeInitializeEvent(&Event, NotificationEvent, FALSE);
793 
794         /* copy stack location */
795         IoCopyCurrentIrpStackLocationToNext (Irp);
796 
797         /* set completion routine */
798         IoSetCompletionRoutine(Irp, MouHid_StartDeviceCompletion, &Event, TRUE, TRUE, TRUE);
799         Irp->IoStatus.Status = 0;
800 
801         /* pass request */
802         Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
803         if (Status == STATUS_PENDING)
804         {
805             KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
806             Status = Irp->IoStatus.Status;
807         }
808 
809         if (!NT_SUCCESS(Status))
810         {
811             /* failed */
812             Irp->IoStatus.Status = Status;
813             IoCompleteRequest(Irp, IO_NO_INCREMENT);
814             return Status;
815         }
816 
817         /* lets start the device */
818         Status = MouHid_StartDevice(DeviceObject);
819         DPRINT1("MouHid_StartDevice %x\n", Status);
820 
821         /* complete request */
822         Irp->IoStatus.Status = Status;
823         IoCompleteRequest(Irp, IO_NO_INCREMENT);
824 
825         /* done */
826         return Status;
827     }
828     else
829     {
830         /* skip irp stack location */
831         IoSkipCurrentIrpStackLocation(Irp);
832 
833         /* dispatch to lower device */
834         return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
835     }
836 }
837 
838 NTSTATUS
839 NTAPI
840 MouHid_AddDevice(
841     IN PDRIVER_OBJECT DriverObject,
842     IN PDEVICE_OBJECT PhysicalDeviceObject)
843 {
844     NTSTATUS Status;
845     PDEVICE_OBJECT DeviceObject, NextDeviceObject;
846     PMOUHID_DEVICE_EXTENSION DeviceExtension;
847     POWER_STATE State;
848 
849     /* create device object */
850     Status = IoCreateDevice(DriverObject, sizeof(MOUHID_DEVICE_EXTENSION), NULL, FILE_DEVICE_MOUSE, 0, FALSE, &DeviceObject);
851     if (!NT_SUCCESS(Status))
852     {
853         /* failed to create device object */
854         return Status;
855     }
856 
857     /* now attach it */
858     NextDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject);
859     if (!NextDeviceObject)
860     {
861         /* failed to attach */
862         IoDeleteDevice(DeviceObject);
863         return STATUS_DEVICE_NOT_CONNECTED;
864     }
865 
866     /* get device extension */
867     DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
868 
869     /* zero extension */
870     RtlZeroMemory(DeviceExtension, sizeof(MOUHID_DEVICE_EXTENSION));
871 
872     /* init device extension */
873     DeviceExtension->MouseIdentifier = MOUSE_HID_HARDWARE;
874     DeviceExtension->WheelUsagePage = 0;
875     DeviceExtension->NextDeviceObject = NextDeviceObject;
876     KeInitializeEvent(&DeviceExtension->ReadCompletionEvent, NotificationEvent, FALSE);
877     DeviceExtension->Irp = IoAllocateIrp(NextDeviceObject->StackSize, FALSE);
878 
879     /* FIXME handle allocation error */
880     ASSERT(DeviceExtension->Irp);
881 
882     /* FIXME query parameter 'FlipFlopWheel', 'WheelScalingFactor' */
883 
884     /* set power state to D0 */
885     State.DeviceState =  PowerDeviceD0;
886     PoSetPowerState(DeviceObject, DevicePowerState, State);
887 
888     /* init device object */
889     DeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
890     DeviceObject->Flags  &= ~DO_DEVICE_INITIALIZING;
891 
892     /* completed successfully */
893     return STATUS_SUCCESS;
894 }
895 
896 VOID
897 NTAPI
898 MouHid_Unload(
899     IN PDRIVER_OBJECT DriverObject)
900 {
901     UNIMPLEMENTED
902 }
903 
904 
905 NTSTATUS
906 NTAPI
907 DriverEntry(
908     IN PDRIVER_OBJECT DriverObject,
909     IN PUNICODE_STRING RegPath)
910 {
911     /* FIXME check for parameters 'UseOnlyMice', 'TreatAbsoluteAsRelative', 'TreatAbsolutePointerAsAbsolute' */
912 
913     /* initialize driver object */
914     DriverObject->DriverUnload = MouHid_Unload;
915     DriverObject->DriverExtension->AddDevice = MouHid_AddDevice;
916     DriverObject->MajorFunction[IRP_MJ_CREATE] = MouHid_Create;
917     DriverObject->MajorFunction[IRP_MJ_CLOSE] = MouHid_Close;
918     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MouHid_DeviceControl;
919     DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MouHid_InternalDeviceControl;
920     DriverObject->MajorFunction[IRP_MJ_POWER] = MouHid_Power;
921     DriverObject->MajorFunction[IRP_MJ_PNP] = MouHid_Pnp;
922     DriverObject->DriverUnload = MouHid_Unload;
923     DriverObject->DriverExtension->AddDevice = MouHid_AddDevice;
924 
925     /* done */
926     return STATUS_SUCCESS;
927 }
928