[KBDHID][MOUHID]
[reactos.git] / reactos / drivers / hid / mouhid / mouhid.c
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 0xFF, /* unused */
16 MOUSE_LEFT_BUTTON_DOWN,
17 MOUSE_RIGHT_BUTTON_DOWN,
18 MOUSE_MIDDLE_BUTTON_DOWN,
19 MOUSE_BUTTON_4_DOWN,
20 MOUSE_BUTTON_5_DOWN
21 };
22
23 static USHORT MouHid_ButtonDownFlags[] =
24 {
25 0xFF, /* unused */
26 MOUSE_LEFT_BUTTON_UP,
27 MOUSE_RIGHT_BUTTON_UP,
28 MOUSE_MIDDLE_BUTTON_UP,
29 MOUSE_BUTTON_4_UP,
30 MOUSE_BUTTON_5_UP
31 };
32
33 VOID
34 MouHid_GetButtonMove(
35 IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
36 OUT PLONG LastX,
37 OUT PLONG LastY)
38 {
39 NTSTATUS Status;
40 ULONG ValueX, ValueY;
41
42 /* init result */
43 *LastX = 0;
44 *LastY = 0;
45
46 /* get scaled usage value x */
47 Status = HidP_GetScaledUsageValue(HidP_Input,
48 HID_USAGE_PAGE_GENERIC,
49 HIDP_LINK_COLLECTION_UNSPECIFIED,
50 HID_USAGE_GENERIC_X,
51 LastX,
52 DeviceExtension->PreparsedData,
53 DeviceExtension->Report,
54 DeviceExtension->ReportLength);
55 if (Status != HIDP_STATUS_SUCCESS)
56 {
57 /* FIXME: handle more errors */
58 if (Status == HIDP_STATUS_BAD_LOG_PHY_VALUES)
59 {
60 /* FIXME: assume it operates in absolute mode */
61 DeviceExtension->MouseAbsolute = TRUE;
62
63 /* get unscaled value */
64 Status = HidP_GetUsageValue(HidP_Input,
65 HID_USAGE_PAGE_GENERIC,
66 HIDP_LINK_COLLECTION_UNSPECIFIED,
67 HID_USAGE_GENERIC_X,
68 &ValueX,
69 DeviceExtension->PreparsedData,
70 DeviceExtension->Report,
71 DeviceExtension->ReportLength);
72
73 /* FIXME handle error */
74 ASSERT(Status == HIDP_STATUS_SUCCESS);
75
76 /* absolute pointing devices values need be in range 0 - 0xffff */
77 ASSERT(DeviceExtension->ValueCapsX.LogicalMax > 0);
78
79 *LastX = (ValueX * 0xFFFF) / DeviceExtension->ValueCapsX.LogicalMax;
80 }
81 }
82
83 /* get scaled usage value y */
84 Status = HidP_GetScaledUsageValue(HidP_Input,
85 HID_USAGE_PAGE_GENERIC,
86 HIDP_LINK_COLLECTION_UNSPECIFIED,
87 HID_USAGE_GENERIC_Y,
88 LastY,
89 DeviceExtension->PreparsedData,
90 DeviceExtension->Report,
91 DeviceExtension->ReportLength);
92 if (Status != HIDP_STATUS_SUCCESS)
93 {
94 // FIXME: handle more errors
95 if (Status == HIDP_STATUS_BAD_LOG_PHY_VALUES)
96 {
97 // assume it operates in absolute mode
98 DeviceExtension->MouseAbsolute = TRUE;
99
100 // get unscaled value
101 Status = HidP_GetUsageValue(HidP_Input,
102 HID_USAGE_PAGE_GENERIC,
103 HIDP_LINK_COLLECTION_UNSPECIFIED,
104 HID_USAGE_GENERIC_Y,
105 &ValueY,
106 DeviceExtension->PreparsedData,
107 DeviceExtension->Report,
108 DeviceExtension->ReportLength);
109
110 /* FIXME handle error */
111 ASSERT(Status == HIDP_STATUS_SUCCESS);
112
113 /* absolute pointing devices values need be in range 0 - 0xffff */
114 ASSERT(DeviceExtension->ValueCapsY.LogicalMax);
115 *LastY = (ValueY * 0xFFFF) / DeviceExtension->ValueCapsY.LogicalMax;
116 }
117 }
118 }
119
120 VOID
121 MouHid_GetButtonFlags(
122 IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
123 OUT PUSHORT ButtonFlags,
124 OUT PUSHORT Flags)
125 {
126 NTSTATUS Status;
127 USAGE Usage;
128 ULONG Index;
129 PUSAGE TempList;
130 ULONG CurrentUsageListLength;
131
132 /* init flags */
133 *ButtonFlags = 0;
134 *Flags = 0;
135
136 /* get usages */
137 CurrentUsageListLength = DeviceExtension->UsageListLength;
138 Status = HidP_GetUsages(HidP_Input,
139 HID_USAGE_PAGE_BUTTON,
140 HIDP_LINK_COLLECTION_UNSPECIFIED,
141 DeviceExtension->CurrentUsageList,
142 &CurrentUsageListLength,
143 DeviceExtension->PreparsedData,
144 DeviceExtension->Report,
145 DeviceExtension->ReportLength);
146 if (Status != HIDP_STATUS_SUCCESS)
147 {
148 DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status);
149 return;
150 }
151
152 /* extract usage list difference */
153 Status = HidP_UsageListDifference(DeviceExtension->PreviousUsageList,
154 DeviceExtension->CurrentUsageList,
155 DeviceExtension->BreakUsageList,
156 DeviceExtension->MakeUsageList,
157 DeviceExtension->UsageListLength);
158 if (Status != HIDP_STATUS_SUCCESS)
159 {
160 DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status);
161 return;
162 }
163
164 if (DeviceExtension->UsageListLength)
165 {
166 Index = 0;
167 do
168 {
169 /* get usage */
170 Usage = DeviceExtension->BreakUsageList[Index];
171 if (!Usage)
172 break;
173
174 if (Usage <= 5)
175 {
176 /* max 5 buttons supported */
177 *ButtonFlags |= MouHid_ButtonDownFlags[Usage];
178 }
179
180 /* move to next index*/
181 Index++;
182 }while(Index < DeviceExtension->UsageListLength);
183 }
184
185 if (DeviceExtension->UsageListLength)
186 {
187 Index = 0;
188 do
189 {
190 /* get usage */
191 Usage = DeviceExtension->MakeUsageList[Index];
192 if (!Usage)
193 break;
194
195 if (Usage <= 5)
196 {
197 /* max 5 buttons supported */
198 *ButtonFlags |= MouHid_ButtonUpFlags[Usage];
199 }
200
201 /* move to next index*/
202 Index++;
203 }while(Index < DeviceExtension->UsageListLength);
204 }
205
206 /* now switch the previous list with current list */
207 TempList = DeviceExtension->CurrentUsageList;
208 DeviceExtension->CurrentUsageList = DeviceExtension->PreviousUsageList;
209 DeviceExtension->PreviousUsageList = TempList;
210
211 if (DeviceExtension->MouseAbsolute)
212 {
213 // mouse operates absolute
214 *Flags |= MOUSE_MOVE_ABSOLUTE;
215 }
216 }
217
218 VOID
219 MouHid_DispatchInputData(
220 IN PMOUHID_DEVICE_EXTENSION DeviceExtension,
221 IN PMOUSE_INPUT_DATA InputData)
222 {
223 KIRQL OldIrql;
224 ULONG InputDataConsumed;
225
226 if (!DeviceExtension->ClassService)
227 return;
228
229 /* sanity check */
230 ASSERT(DeviceExtension->ClassService);
231 ASSERT(DeviceExtension->ClassDeviceObject);
232
233 /* raise irql */
234 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
235
236 /* dispatch input data */
237 (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(DeviceExtension->ClassDeviceObject, InputData, InputData + 1, &InputDataConsumed);
238
239 /* lower irql to previous level */
240 KeLowerIrql(OldIrql);
241 }
242
243 NTSTATUS
244 NTAPI
245 MouHid_ReadCompletion(
246 IN PDEVICE_OBJECT DeviceObject,
247 IN PIRP Irp,
248 IN PVOID Context)
249 {
250 PMOUHID_DEVICE_EXTENSION DeviceExtension;
251 USHORT ButtonFlags;
252 LONG UsageValue;
253 NTSTATUS Status;
254 LONG LastX, LastY;
255 MOUSE_INPUT_DATA MouseInputData;
256 USHORT Flags;
257
258 /* get device extension */
259 DeviceExtension = Context;
260
261 if (Irp->IoStatus.Status == STATUS_PRIVILEGE_NOT_HELD ||
262 Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED ||
263 Irp->IoStatus.Status == STATUS_CANCELLED ||
264 DeviceExtension->StopReadReport)
265 {
266 /* failed to read or should be stopped*/
267 DPRINT1("[MOUHID] ReadCompletion terminating read Status %x\n", Irp->IoStatus.Status);
268
269 /* report no longer active */
270 DeviceExtension->ReadReportActive = FALSE;
271
272 /* request stopping of the report cycle */
273 DeviceExtension->StopReadReport = FALSE;
274
275 /* signal completion event */
276 KeSetEvent(&DeviceExtension->ReadCompletionEvent, 0, 0);
277 return STATUS_MORE_PROCESSING_REQUIRED;
278 }
279
280 /* get mouse change */
281 MouHid_GetButtonMove(DeviceExtension, &LastX, &LastY);
282
283 /* get mouse change flags */
284 MouHid_GetButtonFlags(DeviceExtension, &ButtonFlags, &Flags);
285
286 /* init input data */
287 RtlZeroMemory(&MouseInputData, sizeof(MOUSE_INPUT_DATA));
288
289 /* init input data */
290 MouseInputData.ButtonFlags = ButtonFlags;
291 MouseInputData.Flags = Flags;
292 MouseInputData.LastX = LastX;
293 MouseInputData.LastY = LastY;
294
295 /* detect mouse wheel change */
296 if (DeviceExtension->MouseIdentifier == WHEELMOUSE_HID_HARDWARE)
297 {
298 /* get usage */
299 UsageValue = 0;
300 Status = HidP_GetScaledUsageValue(HidP_Input,
301 HID_USAGE_PAGE_GENERIC,
302 HIDP_LINK_COLLECTION_UNSPECIFIED,
303 HID_USAGE_GENERIC_WHEEL,
304 &UsageValue,
305 DeviceExtension->PreparsedData,
306 DeviceExtension->Report,
307 DeviceExtension->ReportLength);
308 if (Status == HIDP_STATUS_SUCCESS && UsageValue != 0)
309 {
310 /* store wheel status */
311 MouseInputData.ButtonFlags |= MOUSE_WHEEL;
312 MouseInputData.ButtonData = (USHORT)(UsageValue * WHEEL_DELTA);
313 }
314 else
315 {
316 DPRINT("[MOUHID] failed to get wheel status with %x\n", Status);
317 }
318 }
319
320 DPRINT("[MOUHID] ReportData %02x %02x %02x %02x %02x %02x %02x\n",
321 DeviceExtension->Report[0] & 0xFF,
322 DeviceExtension->Report[1] & 0xFF, DeviceExtension->Report[2] & 0xFF,
323 DeviceExtension->Report[3] & 0xFF, DeviceExtension->Report[4] & 0xFF,
324 DeviceExtension->Report[5] & 0xFF, DeviceExtension->Report[6] & 0xFF);
325
326 DPRINT("[MOUHID] LastX %ld LastY %ld Flags %x ButtonData %x\n", MouseInputData.LastX, MouseInputData.LastY, MouseInputData.ButtonFlags, MouseInputData.ButtonData);
327
328 /* dispatch mouse action */
329 MouHid_DispatchInputData(DeviceExtension, &MouseInputData);
330
331 /* re-init read */
332 MouHid_InitiateRead(DeviceExtension);
333
334 /* stop completion */
335 return STATUS_MORE_PROCESSING_REQUIRED;
336 }
337
338 NTSTATUS
339 MouHid_InitiateRead(
340 IN PMOUHID_DEVICE_EXTENSION DeviceExtension)
341 {
342 PIO_STACK_LOCATION IoStack;
343 NTSTATUS Status;
344
345 /* re-use irp */
346 IoReuseIrp(DeviceExtension->Irp, STATUS_SUCCESS);
347
348 /* init irp */
349 DeviceExtension->Irp->MdlAddress = DeviceExtension->ReportMDL;
350
351 /* get next stack location */
352 IoStack = IoGetNextIrpStackLocation(DeviceExtension->Irp);
353
354 /* init stack location */
355 IoStack->Parameters.Read.Length = DeviceExtension->ReportLength;
356 IoStack->Parameters.Read.Key = 0;
357 IoStack->Parameters.Read.ByteOffset.QuadPart = 0LL;
358 IoStack->MajorFunction = IRP_MJ_READ;
359 IoStack->FileObject = DeviceExtension->FileObject;
360
361 /* set completion routine */
362 IoSetCompletionRoutine(DeviceExtension->Irp, MouHid_ReadCompletion, DeviceExtension, TRUE, TRUE, TRUE);
363
364 /* read is active */
365 DeviceExtension->ReadReportActive = TRUE;
366
367 /* start the read */
368 Status = IoCallDriver(DeviceExtension->NextDeviceObject, DeviceExtension->Irp);
369
370 /* done */
371 return Status;
372 }
373
374 NTSTATUS
375 NTAPI
376 MouHid_CreateCompletion(
377 IN PDEVICE_OBJECT DeviceObject,
378 IN PIRP Irp,
379 IN PVOID Context)
380 {
381 KeSetEvent(Context, 0, FALSE);
382 return STATUS_MORE_PROCESSING_REQUIRED;
383 }
384
385
386 NTSTATUS
387 NTAPI
388 MouHid_Create(
389 IN PDEVICE_OBJECT DeviceObject,
390 IN PIRP Irp)
391 {
392 PIO_STACK_LOCATION IoStack;
393 NTSTATUS Status;
394 KEVENT Event;
395 PMOUHID_DEVICE_EXTENSION DeviceExtension;
396
397 DPRINT("MOUHID: IRP_MJ_CREATE\n");
398
399 /* get device extension */
400 DeviceExtension = DeviceObject->DeviceExtension;
401
402 /* get stack location */
403 IoStack = IoGetCurrentIrpStackLocation(Irp);
404
405 /* copy stack location to next */
406 IoCopyCurrentIrpStackLocationToNext(Irp);
407
408 /* init event */
409 KeInitializeEvent(&Event, NotificationEvent, FALSE);
410
411 /* prepare irp */
412 IoSetCompletionRoutine(Irp, MouHid_CreateCompletion, &Event, TRUE, TRUE, TRUE);
413
414 /* call lower driver */
415 Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
416 if (Status == STATUS_PENDING)
417 {
418 /* request pending */
419 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
420 }
421
422 /* check for success */
423 if (!NT_SUCCESS(Status))
424 {
425 /* failed */
426 Irp->IoStatus.Status = Status;
427 IoCompleteRequest(Irp, IO_NO_INCREMENT);
428 return Status;
429 }
430
431 /* is the driver already in use */
432 if (DeviceExtension->FileObject == NULL)
433 {
434 /* did the caller specify correct attributes */
435 ASSERT(IoStack->Parameters.Create.SecurityContext);
436 if (IoStack->Parameters.Create.SecurityContext->DesiredAccess)
437 {
438 /* store file object */
439 DeviceExtension->FileObject = IoStack->FileObject;
440
441 /* reset event */
442 KeResetEvent(&DeviceExtension->ReadCompletionEvent);
443
444 /* initiating read */
445 Status = MouHid_InitiateRead(DeviceExtension);
446 DPRINT("[MOUHID] MouHid_InitiateRead: status %x\n", Status);
447 if (Status == STATUS_PENDING)
448 {
449 /* report irp is pending */
450 Status = STATUS_SUCCESS;
451 }
452 }
453 }
454
455 /* complete request */
456 Irp->IoStatus.Status = Status;
457 IoCompleteRequest(Irp, IO_NO_INCREMENT);
458 return Status;
459 }
460
461
462 NTSTATUS
463 NTAPI
464 MouHid_Close(
465 IN PDEVICE_OBJECT DeviceObject,
466 IN PIRP Irp)
467 {
468 PMOUHID_DEVICE_EXTENSION DeviceExtension;
469
470 /* get device extension */
471 DeviceExtension = DeviceObject->DeviceExtension;
472
473 DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive);
474
475 if (DeviceExtension->ReadReportActive)
476 {
477 /* request stopping of the report cycle */
478 DeviceExtension->StopReadReport = TRUE;
479
480 /* wait until the reports have been read */
481 KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL);
482
483 /* cancel irp */
484 IoCancelIrp(DeviceExtension->Irp);
485 }
486
487 DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive);
488
489 /* remove file object */
490 DeviceExtension->FileObject = NULL;
491
492 /* skip location */
493 IoSkipCurrentIrpStackLocation(Irp);
494
495 /* pass irp to down the stack */
496 return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
497 }
498
499 NTSTATUS
500 NTAPI
501 MouHid_InternalDeviceControl(
502 IN PDEVICE_OBJECT DeviceObject,
503 IN PIRP Irp)
504 {
505 PIO_STACK_LOCATION IoStack;
506 PMOUSE_ATTRIBUTES Attributes;
507 PMOUHID_DEVICE_EXTENSION DeviceExtension;
508 PCONNECT_DATA Data;
509
510 /* get current stack location */
511 IoStack = IoGetCurrentIrpStackLocation(Irp);
512
513 DPRINT("[MOUHID] InternalDeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
514
515 /* get device extension */
516 DeviceExtension = DeviceObject->DeviceExtension;
517
518 /* handle requests */
519 switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
520 {
521 case IOCTL_MOUSE_QUERY_ATTRIBUTES:
522 /* verify output buffer length */
523 if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES))
524 {
525 /* invalid request */
526 DPRINT1("[MOUHID] IOCTL_MOUSE_QUERY_ATTRIBUTES Buffer too small\n");
527 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
528 IoCompleteRequest(Irp, IO_NO_INCREMENT);
529 return STATUS_BUFFER_TOO_SMALL;
530 }
531
532 /* get output buffer */
533 Attributes = Irp->AssociatedIrp.SystemBuffer;
534
535 /* type of mouse */
536 Attributes->MouseIdentifier = DeviceExtension->MouseIdentifier;
537
538 /* number of buttons */
539 Attributes->NumberOfButtons = DeviceExtension->UsageListLength;
540
541 /* sample rate not used for usb */
542 Attributes->SampleRate = 0;
543
544 /* queue length */
545 Attributes->InputDataQueueLength = 2;
546
547 DPRINT("[MOUHID] MouseIdentifier %x\n", Attributes->MouseIdentifier);
548 DPRINT("[MOUHID] NumberOfButtons %x\n", Attributes->NumberOfButtons);
549 DPRINT("[MOUHID] SampleRate %x\n", Attributes->SampleRate);
550 DPRINT("[MOUHID] InputDataQueueLength %x\n", Attributes->InputDataQueueLength);
551
552 /* complete request */
553 Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
554 Irp->IoStatus.Status = STATUS_SUCCESS;
555 IoCompleteRequest(Irp, IO_NO_INCREMENT);
556 return STATUS_SUCCESS;
557
558 case IOCTL_INTERNAL_MOUSE_CONNECT:
559 /* verify input buffer length */
560 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA))
561 {
562 /* invalid request */
563 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
564 IoCompleteRequest(Irp, IO_NO_INCREMENT);
565 return STATUS_INVALID_PARAMETER;
566 }
567
568 /* is it already connected */
569 if (DeviceExtension->ClassService)
570 {
571 /* already connected */
572 Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
573 IoCompleteRequest(Irp, IO_NO_INCREMENT);
574 return STATUS_SHARING_VIOLATION;
575 }
576
577 /* get connect data */
578 Data = IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
579
580 /* store connect details */
581 DeviceExtension->ClassDeviceObject = Data->ClassDeviceObject;
582 DeviceExtension->ClassService = Data->ClassService;
583
584 /* completed successfully */
585 Irp->IoStatus.Status = STATUS_SUCCESS;
586 IoCompleteRequest(Irp, IO_NO_INCREMENT);
587 return STATUS_SUCCESS;
588
589 case IOCTL_INTERNAL_MOUSE_DISCONNECT:
590 /* not supported */
591 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
592 IoCompleteRequest(Irp, IO_NO_INCREMENT);
593 return STATUS_NOT_IMPLEMENTED;
594
595 case IOCTL_INTERNAL_MOUSE_ENABLE:
596 /* not supported */
597 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
598 IoCompleteRequest(Irp, IO_NO_INCREMENT);
599 return STATUS_NOT_SUPPORTED;
600
601 case IOCTL_INTERNAL_MOUSE_DISABLE:
602 /* not supported */
603 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
604 IoCompleteRequest(Irp, IO_NO_INCREMENT);
605 return STATUS_INVALID_DEVICE_REQUEST;
606 }
607
608 DPRINT1("[MOUHID] Unknown DeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
609 /* unknown request not supported */
610 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
611 IoCompleteRequest(Irp, IO_NO_INCREMENT);
612 return STATUS_NOT_SUPPORTED;
613 }
614
615 NTSTATUS
616 NTAPI
617 MouHid_DeviceControl(
618 IN PDEVICE_OBJECT DeviceObject,
619 IN PIRP Irp)
620 {
621 PMOUHID_DEVICE_EXTENSION DeviceExtension;
622
623 /* get device extension */
624 DeviceExtension = DeviceObject->DeviceExtension;
625
626 /* skip stack location */
627 IoSkipCurrentIrpStackLocation(Irp);
628
629 /* pass and forget */
630 return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
631 }
632
633 NTSTATUS
634 NTAPI
635 MouHid_Power(
636 IN PDEVICE_OBJECT DeviceObject,
637 IN PIRP Irp)
638 {
639 PMOUHID_DEVICE_EXTENSION DeviceExtension;
640
641 DeviceExtension = DeviceObject->DeviceExtension;
642 PoStartNextPowerIrp(Irp);
643 IoSkipCurrentIrpStackLocation(Irp);
644 return PoCallDriver(DeviceExtension->NextDeviceObject, Irp);
645 }
646
647 NTSTATUS
648 NTAPI
649 MouHid_SystemControl(
650 IN PDEVICE_OBJECT DeviceObject,
651 IN PIRP Irp)
652 {
653 PMOUHID_DEVICE_EXTENSION DeviceExtension;
654
655 DeviceExtension = DeviceObject->DeviceExtension;
656 IoSkipCurrentIrpStackLocation(Irp);
657 return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
658 }
659
660 NTSTATUS
661 MouHid_SubmitRequest(
662 PDEVICE_OBJECT DeviceObject,
663 ULONG IoControlCode,
664 ULONG InputBufferSize,
665 PVOID InputBuffer,
666 ULONG OutputBufferSize,
667 PVOID OutputBuffer)
668 {
669 KEVENT Event;
670 PMOUHID_DEVICE_EXTENSION DeviceExtension;
671 PIRP Irp;
672 NTSTATUS Status;
673 IO_STATUS_BLOCK IoStatus;
674
675 /* get device extension */
676 DeviceExtension = DeviceObject->DeviceExtension;
677
678 /* init event */
679 KeInitializeEvent(&Event, NotificationEvent, FALSE);
680
681 /* build request */
682 Irp = IoBuildDeviceIoControlRequest(IoControlCode,
683 DeviceExtension->NextDeviceObject,
684 InputBuffer,
685 InputBufferSize,
686 OutputBuffer,
687 OutputBufferSize,
688 FALSE,
689 &Event,
690 &IoStatus);
691 if (!Irp)
692 {
693 /* no memory */
694 return STATUS_INSUFFICIENT_RESOURCES;
695 }
696
697 /* send request */
698 Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
699 if (Status == STATUS_PENDING)
700 {
701 /* wait for request to complete */
702 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
703 Status = IoStatus.Status;
704 }
705
706 /* done */
707 return Status;
708 }
709
710 NTSTATUS
711 NTAPI
712 MouHid_StartDevice(
713 IN PDEVICE_OBJECT DeviceObject)
714 {
715 NTSTATUS Status;
716 ULONG Buttons;
717 HID_COLLECTION_INFORMATION Information;
718 PVOID PreparsedData;
719 HIDP_CAPS Capabilities;
720 ULONG ValueCapsLength;
721 HIDP_VALUE_CAPS ValueCaps;
722 PMOUHID_DEVICE_EXTENSION DeviceExtension;
723 PUSAGE Buffer;
724
725 /* get device extension */
726 DeviceExtension = DeviceObject->DeviceExtension;
727
728 /* query collection information */
729 Status = MouHid_SubmitRequest(DeviceObject,
730 IOCTL_HID_GET_COLLECTION_INFORMATION,
731 0,
732 NULL,
733 sizeof(HID_COLLECTION_INFORMATION),
734 &Information);
735 if (!NT_SUCCESS(Status))
736 {
737 /* failed to query collection information */
738 DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status);
739 return Status;
740 }
741
742 /* lets allocate space for preparsed data */
743 PreparsedData = ExAllocatePoolWithTag(NonPagedPool, Information.DescriptorSize, MOUHID_TAG);
744 if (!PreparsedData)
745 {
746 /* no memory */
747 DPRINT1("[MOUHID] no memory size %u\n", Information.DescriptorSize);
748 return STATUS_INSUFFICIENT_RESOURCES;
749 }
750
751 /* now obtain the preparsed data */
752 Status = MouHid_SubmitRequest(DeviceObject,
753 IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
754 0,
755 NULL,
756 Information.DescriptorSize,
757 PreparsedData);
758 if (!NT_SUCCESS(Status))
759 {
760 /* failed to get preparsed data */
761 DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status);
762 ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
763 return Status;
764 }
765
766 /* lets get the caps */
767 Status = HidP_GetCaps(PreparsedData, &Capabilities);
768 if (Status != HIDP_STATUS_SUCCESS)
769 {
770 /* failed to get capabilities */
771 DPRINT1("[MOUHID] failed to obtain caps with %x\n", Status);
772 ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
773 return Status;
774 }
775
776 DPRINT("[MOUHID] Usage %x UsagePage %x InputReportLength %lu\n", Capabilities.Usage, Capabilities.UsagePage, Capabilities.InputReportByteLength);
777
778 /* verify capabilities */
779 if ((Capabilities.Usage != HID_USAGE_GENERIC_POINTER && Capabilities.Usage != HID_USAGE_GENERIC_MOUSE) || Capabilities.UsagePage != HID_USAGE_PAGE_GENERIC)
780 {
781 /* not supported */
782 ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
783 return STATUS_UNSUCCESSFUL;
784 }
785
786 /* init input report */
787 DeviceExtension->ReportLength = Capabilities.InputReportByteLength;
788 ASSERT(DeviceExtension->ReportLength);
789 DeviceExtension->Report = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->ReportLength, MOUHID_TAG);
790 ASSERT(DeviceExtension->Report);
791 RtlZeroMemory(DeviceExtension->Report, DeviceExtension->ReportLength);
792
793 /* build mdl */
794 DeviceExtension->ReportMDL = IoAllocateMdl(DeviceExtension->Report,
795 DeviceExtension->ReportLength,
796 FALSE,
797 FALSE,
798 NULL);
799 ASSERT(DeviceExtension->ReportMDL);
800
801 /* init mdl */
802 MmBuildMdlForNonPagedPool(DeviceExtension->ReportMDL);
803
804 /* get max number of buttons */
805 Buttons = HidP_MaxUsageListLength(HidP_Input,
806 HID_USAGE_PAGE_BUTTON,
807 PreparsedData);
808 DPRINT("[MOUHID] Buttons %lu\n", Buttons);
809 ASSERT(Buttons > 0);
810
811 /* now allocate an array for those buttons */
812 Buffer = ExAllocatePoolWithTag(NonPagedPool, sizeof(USAGE) * 4 * Buttons, MOUHID_TAG);
813 if (!Buffer)
814 {
815 /* no memory */
816 ExFreePoolWithTag(PreparsedData, MOUHID_TAG);
817 return STATUS_INSUFFICIENT_RESOURCES;
818 }
819 DeviceExtension->UsageListBuffer = Buffer;
820
821 /* init usage lists */
822 RtlZeroMemory(Buffer, sizeof(USAGE) * 4 * Buttons);
823 DeviceExtension->CurrentUsageList = Buffer;
824 Buffer += Buttons;
825 DeviceExtension->PreviousUsageList = Buffer;
826 Buffer += Buttons;
827 DeviceExtension->MakeUsageList = Buffer;
828 Buffer += Buttons;
829 DeviceExtension->BreakUsageList = Buffer;
830
831 /* store number of buttons */
832 DeviceExtension->UsageListLength = (USHORT)Buttons;
833
834 /* store preparsed data */
835 DeviceExtension->PreparsedData = PreparsedData;
836
837 ValueCapsLength = 1;
838 HidP_GetSpecificValueCaps(HidP_Input,
839 HID_USAGE_PAGE_GENERIC,
840 HIDP_LINK_COLLECTION_UNSPECIFIED,
841 HID_USAGE_GENERIC_X,
842 &DeviceExtension->ValueCapsX,
843 &ValueCapsLength,
844 PreparsedData);
845
846 ValueCapsLength = 1;
847 HidP_GetSpecificValueCaps(HidP_Input,
848 HID_USAGE_PAGE_GENERIC,
849 HIDP_LINK_COLLECTION_UNSPECIFIED,
850 HID_USAGE_GENERIC_Y,
851 &DeviceExtension->ValueCapsY,
852 &ValueCapsLength,
853 PreparsedData);
854
855 /* now check for wheel mouse support */
856 ValueCapsLength = 1;
857 Status = HidP_GetSpecificValueCaps(HidP_Input,
858 HID_USAGE_PAGE_GENERIC,
859 HIDP_LINK_COLLECTION_UNSPECIFIED,
860 HID_USAGE_GENERIC_WHEEL,
861 &ValueCaps,
862 &ValueCapsLength,
863 PreparsedData);
864 if (Status == HIDP_STATUS_SUCCESS )
865 {
866 /* mouse has wheel support */
867 DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
868 DeviceExtension->WheelUsagePage = ValueCaps.UsagePage;
869 DPRINT("[MOUHID] mouse wheel support detected\n", Status);
870 }
871 else
872 {
873 /* check if the mouse has z-axis */
874 ValueCapsLength = 1;
875 Status = HidP_GetSpecificValueCaps(HidP_Input,
876 HID_USAGE_PAGE_GENERIC,
877 HIDP_LINK_COLLECTION_UNSPECIFIED,
878 HID_USAGE_GENERIC_Z,
879 &ValueCaps,
880 &ValueCapsLength,
881 PreparsedData);
882 if (Status == HIDP_STATUS_SUCCESS && ValueCapsLength == 1)
883 {
884 /* wheel support */
885 DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE;
886 DeviceExtension->WheelUsagePage = ValueCaps.UsagePage;
887 DPRINT("[MOUHID] mouse wheel support detected with z-axis\n", Status);
888 }
889 }
890
891 /* completed successfully */
892 return STATUS_SUCCESS;
893 }
894
895 NTSTATUS
896 NTAPI
897 MouHid_StartDeviceCompletion(
898 IN PDEVICE_OBJECT DeviceObject,
899 IN PIRP Irp,
900 IN PVOID Context)
901 {
902 KeSetEvent(Context, 0, FALSE);
903 return STATUS_MORE_PROCESSING_REQUIRED;
904 }
905
906 NTSTATUS
907 NTAPI
908 MouHid_FreeResources(
909 IN PDEVICE_OBJECT DeviceObject)
910 {
911 PMOUHID_DEVICE_EXTENSION DeviceExtension;
912
913 /* get device extension */
914 DeviceExtension = DeviceObject->DeviceExtension;
915
916 /* free resources */
917 if (DeviceExtension->PreparsedData)
918 {
919 ExFreePoolWithTag(DeviceExtension->PreparsedData, MOUHID_TAG);
920 DeviceExtension->PreparsedData = NULL;
921 }
922
923 if (DeviceExtension->UsageListBuffer)
924 {
925 ExFreePoolWithTag(DeviceExtension->UsageListBuffer, MOUHID_TAG);
926 DeviceExtension->UsageListBuffer = NULL;
927 DeviceExtension->CurrentUsageList = NULL;
928 DeviceExtension->PreviousUsageList = NULL;
929 DeviceExtension->MakeUsageList = NULL;
930 DeviceExtension->BreakUsageList = NULL;
931 }
932
933 if (DeviceExtension->ReportMDL)
934 {
935 IoFreeMdl(DeviceExtension->ReportMDL);
936 DeviceExtension->ReportMDL = NULL;
937 }
938
939 if (DeviceExtension->Report)
940 {
941 ExFreePoolWithTag(DeviceExtension->Report, MOUHID_TAG);
942 DeviceExtension->Report = NULL;
943 }
944
945 return STATUS_SUCCESS;
946 }
947
948 NTSTATUS
949 NTAPI
950 MouHid_Flush(
951 IN PDEVICE_OBJECT DeviceObject,
952 IN PIRP Irp)
953 {
954 PIO_STACK_LOCATION IoStack;
955 PMOUHID_DEVICE_EXTENSION DeviceExtension;
956
957 /* get device extension */
958 DeviceExtension = DeviceObject->DeviceExtension;
959
960 /* skip current stack location */
961 IoSkipCurrentIrpStackLocation(Irp);
962
963 /* get next stack location */
964 IoStack = IoGetNextIrpStackLocation(Irp);
965
966 /* change request to hid flush queue request */
967 IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
968 IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_FLUSH_QUEUE;
969
970 /* call device */
971 return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
972 }
973
974 NTSTATUS
975 NTAPI
976 MouHid_Pnp(
977 IN PDEVICE_OBJECT DeviceObject,
978 IN PIRP Irp)
979 {
980 PIO_STACK_LOCATION IoStack;
981 KEVENT Event;
982 NTSTATUS Status;
983 PMOUHID_DEVICE_EXTENSION DeviceExtension;
984
985 /* get device extension */
986 DeviceExtension = DeviceObject->DeviceExtension;
987
988 /* get current irp stack */
989 IoStack = IoGetCurrentIrpStackLocation(Irp);
990 DPRINT("[MOUHID] IRP_MJ_PNP Request: %x\n", IoStack->MinorFunction);
991
992 switch (IoStack->MinorFunction)
993 {
994 case IRP_MN_STOP_DEVICE:
995 case IRP_MN_SURPRISE_REMOVAL:
996 /* free resources */
997 MouHid_FreeResources(DeviceObject);
998 case IRP_MN_CANCEL_REMOVE_DEVICE:
999 case IRP_MN_QUERY_STOP_DEVICE:
1000 case IRP_MN_CANCEL_STOP_DEVICE:
1001 case IRP_MN_QUERY_REMOVE_DEVICE:
1002 /* indicate success */
1003 Irp->IoStatus.Status = STATUS_SUCCESS;
1004
1005 /* skip irp stack location */
1006 IoSkipCurrentIrpStackLocation(Irp);
1007
1008 /* dispatch to lower device */
1009 return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
1010
1011 case IRP_MN_REMOVE_DEVICE:
1012 /* FIXME synchronization */
1013
1014 /* request stop */
1015 DeviceExtension->StopReadReport = TRUE;
1016
1017 /* cancel irp */
1018 IoCancelIrp(DeviceExtension->Irp);
1019
1020 /* free resources */
1021 MouHid_FreeResources(DeviceObject);
1022
1023 /* indicate success */
1024 Irp->IoStatus.Status = STATUS_SUCCESS;
1025
1026 /* skip irp stack location */
1027 IoSkipCurrentIrpStackLocation(Irp);
1028
1029 /* dispatch to lower device */
1030 Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
1031
1032 /* wait for completion of stop event */
1033 KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL);
1034
1035 /* free irp */
1036 IoFreeIrp(DeviceExtension->Irp);
1037
1038 /* detach device */
1039 IoDetachDevice(DeviceExtension->NextDeviceObject);
1040
1041 /* delete device */
1042 IoDeleteDevice(DeviceObject);
1043
1044 /* done */
1045 return Status;
1046
1047 case IRP_MN_START_DEVICE:
1048 /* init event */
1049 KeInitializeEvent(&Event, NotificationEvent, FALSE);
1050
1051 /* copy stack location */
1052 IoCopyCurrentIrpStackLocationToNext (Irp);
1053
1054 /* set completion routine */
1055 IoSetCompletionRoutine(Irp, MouHid_StartDeviceCompletion, &Event, TRUE, TRUE, TRUE);
1056 Irp->IoStatus.Status = 0;
1057
1058 /* pass request */
1059 Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
1060 if (Status == STATUS_PENDING)
1061 {
1062 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1063 Status = Irp->IoStatus.Status;
1064 }
1065
1066 if (!NT_SUCCESS(Status))
1067 {
1068 /* failed */
1069 Irp->IoStatus.Status = Status;
1070 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1071 return Status;
1072 }
1073
1074 /* lets start the device */
1075 Status = MouHid_StartDevice(DeviceObject);
1076 DPRINT("MouHid_StartDevice %x\n", Status);
1077
1078 /* complete request */
1079 Irp->IoStatus.Status = Status;
1080 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1081
1082 /* done */
1083 return Status;
1084
1085 default:
1086 /* skip irp stack location */
1087 IoSkipCurrentIrpStackLocation(Irp);
1088
1089 /* dispatch to lower device */
1090 return IoCallDriver(DeviceExtension->NextDeviceObject, Irp);
1091 }
1092 }
1093
1094 NTSTATUS
1095 NTAPI
1096 MouHid_AddDevice(
1097 IN PDRIVER_OBJECT DriverObject,
1098 IN PDEVICE_OBJECT PhysicalDeviceObject)
1099 {
1100 NTSTATUS Status;
1101 PDEVICE_OBJECT DeviceObject, NextDeviceObject;
1102 PMOUHID_DEVICE_EXTENSION DeviceExtension;
1103 POWER_STATE State;
1104
1105 /* create device object */
1106 Status = IoCreateDevice(DriverObject,
1107 sizeof(MOUHID_DEVICE_EXTENSION),
1108 NULL,
1109 FILE_DEVICE_MOUSE,
1110 0,
1111 FALSE,
1112 &DeviceObject);
1113 if (!NT_SUCCESS(Status))
1114 {
1115 /* failed to create device object */
1116 return Status;
1117 }
1118
1119 /* now attach it */
1120 NextDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject);
1121 if (!NextDeviceObject)
1122 {
1123 /* failed to attach */
1124 IoDeleteDevice(DeviceObject);
1125 return STATUS_DEVICE_NOT_CONNECTED;
1126 }
1127
1128 /* get device extension */
1129 DeviceExtension = DeviceObject->DeviceExtension;
1130
1131 /* zero extension */
1132 RtlZeroMemory(DeviceExtension, sizeof(MOUHID_DEVICE_EXTENSION));
1133
1134 /* init device extension */
1135 DeviceExtension->MouseIdentifier = MOUSE_HID_HARDWARE;
1136 DeviceExtension->WheelUsagePage = 0;
1137 DeviceExtension->NextDeviceObject = NextDeviceObject;
1138 KeInitializeEvent(&DeviceExtension->ReadCompletionEvent, NotificationEvent, FALSE);
1139 DeviceExtension->Irp = IoAllocateIrp(NextDeviceObject->StackSize, FALSE);
1140
1141 /* FIXME handle allocation error */
1142 ASSERT(DeviceExtension->Irp);
1143
1144 /* FIXME query parameter 'FlipFlopWheel', 'WheelScalingFactor' */
1145
1146 /* set power state to D0 */
1147 State.DeviceState = PowerDeviceD0;
1148 PoSetPowerState(DeviceObject, DevicePowerState, State);
1149
1150 /* init device object */
1151 DeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
1152 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1153
1154 /* completed successfully */
1155 return STATUS_SUCCESS;
1156 }
1157
1158 VOID
1159 NTAPI
1160 MouHid_Unload(
1161 IN PDRIVER_OBJECT DriverObject)
1162 {
1163 UNIMPLEMENTED
1164 }
1165
1166
1167 NTSTATUS
1168 NTAPI
1169 DriverEntry(
1170 IN PDRIVER_OBJECT DriverObject,
1171 IN PUNICODE_STRING RegPath)
1172 {
1173 /* FIXME check for parameters 'UseOnlyMice', 'TreatAbsoluteAsRelative', 'TreatAbsolutePointerAsAbsolute' */
1174
1175 /* initialize driver object */
1176 DriverObject->DriverUnload = MouHid_Unload;
1177 DriverObject->DriverExtension->AddDevice = MouHid_AddDevice;
1178 DriverObject->MajorFunction[IRP_MJ_CREATE] = MouHid_Create;
1179 DriverObject->MajorFunction[IRP_MJ_CLOSE] = MouHid_Close;
1180 DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = MouHid_Flush;
1181 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MouHid_DeviceControl;
1182 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MouHid_InternalDeviceControl;
1183 DriverObject->MajorFunction[IRP_MJ_POWER] = MouHid_Power;
1184 DriverObject->MajorFunction[IRP_MJ_PNP] = MouHid_Pnp;
1185 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = MouHid_SystemControl;
1186 DriverObject->DriverUnload = MouHid_Unload;
1187 DriverObject->DriverExtension->AddDevice = MouHid_AddDevice;
1188
1189 /* done */
1190 return STATUS_SUCCESS;
1191 }