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