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