1 /* 2 * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: drivers/input/i8042prt/mouse.c 5 * PURPOSE: Mouse specific functions 6 * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) 7 Copyright Jason Filby (jasonfilby@yahoo.com) 8 Copyright Martijn Vernooij (o112w8r02@sneakemail.com) 9 Copyright 2006-2007 Herv� Poussineau (hpoussin@reactos.org) 10 Copyright 2008 Colin Finck (mail@colinfinck.de) 11 */ 12 13 /* INCLUDES ****************************************************************/ 14 15 #include "i8042prt.h" 16 17 #include <debug.h> 18 19 /* FUNCTIONS *****************************************************************/ 20 21 static KDEFERRED_ROUTINE i8042MouDpcRoutine; 22 static KDEFERRED_ROUTINE i8042DpcRoutineMouseTimeout; 23 24 /* 25 * These functions are callbacks for filter driver custom interrupt 26 * service routines. 27 */ 28 static VOID NTAPI 29 i8042MouIsrWritePort( 30 IN PVOID Context, 31 IN UCHAR Value) 32 { 33 PI8042_MOUSE_EXTENSION DeviceExtension; 34 35 DeviceExtension = (PI8042_MOUSE_EXTENSION)Context; 36 37 if (DeviceExtension->MouseHook.IsrWritePort != i8042MouIsrWritePort) 38 { 39 DeviceExtension->MouseHook.IsrWritePort( 40 DeviceExtension->MouseHook.CallContext, 41 Value); 42 } 43 else 44 i8042IsrWritePort(DeviceExtension->Common.PortDeviceExtension, Value, CTRL_WRITE_MOUSE); 45 } 46 47 static VOID NTAPI 48 i8042MouQueuePacket( 49 IN PVOID Context) 50 { 51 PI8042_MOUSE_EXTENSION DeviceExtension; 52 53 DeviceExtension = (PI8042_MOUSE_EXTENSION)Context; 54 55 DeviceExtension->MouseComplete = TRUE; 56 DeviceExtension->MouseInBuffer++; 57 if (DeviceExtension->MouseInBuffer >= DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize) 58 { 59 WARN_(I8042PRT, "Mouse buffer overflow\n"); 60 DeviceExtension->MouseInBuffer--; 61 } 62 63 TRACE_(I8042PRT, "Irq completes mouse packet\n"); 64 KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL); 65 } 66 67 VOID 68 i8042MouHandle( 69 IN PI8042_MOUSE_EXTENSION DeviceExtension, 70 IN UCHAR Output) 71 { 72 PMOUSE_INPUT_DATA MouseInput; 73 CHAR Scroll; 74 75 MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer; 76 77 switch (DeviceExtension->MouseState) 78 { 79 case MouseIdle: 80 /* This bit should be 1, if not drop the packet, we 81 * might be lucky and get in sync again 82 */ 83 if (!(Output & 8)) { 84 WARN_(I8042PRT, "Bad input, dropping..\n"); 85 return; 86 } 87 88 MouseInput->Buttons = 0; 89 MouseInput->RawButtons = 0; 90 MouseInput->Flags = MOUSE_MOVE_RELATIVE; 91 92 /* Note how we ignore the overflow bits, like Windows 93 * is said to do. There's no reasonable thing to do 94 * anyway. 95 */ 96 97 if (Output & 16) 98 MouseInput->LastX = 1; 99 else 100 MouseInput->LastX = 0; 101 if (Output & 32) 102 MouseInput->LastY = 1; 103 else 104 MouseInput->LastY = 0; 105 106 if (Output & 1) 107 MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN; 108 if (Output & 2) 109 MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN; 110 if (Output & 4) 111 MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN; 112 113 DeviceExtension->MouseState = XMovement; 114 break; 115 116 case XMovement: 117 if (MouseInput->LastX) 118 MouseInput->LastX = (LONG) Output - 256; 119 else 120 MouseInput->LastX = Output; 121 122 DeviceExtension->MouseState = YMovement; 123 break; 124 125 case YMovement: 126 if (MouseInput->LastY) 127 MouseInput->LastY = (LONG)Output - 256; 128 else 129 MouseInput->LastY = (LONG)Output; 130 131 /* Windows wants it the other way around */ 132 MouseInput->LastY = -MouseInput->LastY; 133 134 if (DeviceExtension->MouseType == GenericPS2 || 135 DeviceExtension->MouseType == Ps2pp) 136 { 137 i8042MouHandleButtons( 138 DeviceExtension, 139 MOUSE_LEFT_BUTTON_DOWN | 140 MOUSE_RIGHT_BUTTON_DOWN | 141 MOUSE_MIDDLE_BUTTON_DOWN); 142 DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext); 143 DeviceExtension->MouseState = MouseIdle; 144 } 145 else 146 { 147 DeviceExtension->MouseState = ZMovement; 148 } 149 break; 150 151 case ZMovement: 152 Scroll = Output & 0x0f; 153 if (Scroll & 8) 154 Scroll |= 0xf0; 155 156 if (Scroll) 157 { 158 MouseInput->RawButtons |= MOUSE_WHEEL; 159 MouseInput->ButtonData = (USHORT)(Scroll * -WHEEL_DELTA); 160 } 161 162 if (DeviceExtension->MouseType == IntellimouseExplorer) 163 { 164 if (Output & 16) 165 MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN; 166 if (Output & 32) 167 MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN; 168 } 169 i8042MouHandleButtons( 170 DeviceExtension, 171 MOUSE_LEFT_BUTTON_DOWN | 172 MOUSE_RIGHT_BUTTON_DOWN | 173 MOUSE_MIDDLE_BUTTON_DOWN | 174 MOUSE_BUTTON_4_DOWN | 175 MOUSE_BUTTON_5_DOWN); 176 DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext); 177 DeviceExtension->MouseState = MouseIdle; 178 break; 179 180 default: 181 ERR_(I8042PRT, "Unexpected state 0x%lx!\n", DeviceExtension->MouseState); 182 ASSERT(FALSE); 183 } 184 } 185 186 /* 187 * Updates ButtonFlags according to RawButtons and a saved state; 188 * Only takes in account the bits that are set in Mask 189 */ 190 VOID 191 i8042MouHandleButtons( 192 IN PI8042_MOUSE_EXTENSION DeviceExtension, 193 IN USHORT Mask) 194 { 195 PMOUSE_INPUT_DATA MouseInput; 196 USHORT NewButtonData; 197 USHORT ButtonDiff; 198 199 MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer; 200 NewButtonData = (USHORT)(MouseInput->RawButtons & Mask); 201 ButtonDiff = (NewButtonData ^ DeviceExtension->MouseButtonState) & Mask; 202 203 /* Note that the defines are such: 204 * MOUSE_LEFT_BUTTON_DOWN 1 205 * MOUSE_LEFT_BUTTON_UP 2 206 */ 207 MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) | 208 (((~(NewButtonData)) << 1) & (ButtonDiff << 1)) | 209 (MouseInput->RawButtons & 0xfc00); 210 211 INFO_(I8042PRT, "Left raw/up/down: %u/%u/%u\n", 212 MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN, 213 MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN, 214 MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP); 215 216 DeviceExtension->MouseButtonState = 217 (DeviceExtension->MouseButtonState & ~Mask) | (NewButtonData & Mask); 218 } 219 220 /* Does final initializations for the mouse. This method 221 * is called just before connecting the interrupt. 222 */ 223 NTSTATUS 224 i8042MouInitialize( 225 IN PI8042_MOUSE_EXTENSION DeviceExtension) 226 { 227 NTSTATUS Status; 228 UCHAR Value; 229 230 /* Enable the PS/2 mouse port */ 231 i8042Write(DeviceExtension->Common.PortDeviceExtension, DeviceExtension->Common.PortDeviceExtension->ControlPort, MOUSE_ENAB); 232 233 /* Enable the mouse */ 234 if(!i8042IsrWritePort(DeviceExtension->Common.PortDeviceExtension, MOU_ENAB, CTRL_WRITE_MOUSE)) 235 { 236 WARN_(I8042PRT, "Failed to enable mouse!\n"); 237 return STATUS_IO_DEVICE_ERROR; 238 } 239 240 Status = i8042ReadDataWait(DeviceExtension->Common.PortDeviceExtension, &Value); 241 if (!NT_SUCCESS(Status)) 242 { 243 WARN_(I8042PRT, "Failed to read the response of MOU_ENAB, status 0x%08lx\n", Status); 244 return Status; 245 } 246 247 if(Value == MOUSE_ACK) 248 { 249 INFO_(I8042PRT, "Mouse was enabled successfully!\n"); 250 return STATUS_SUCCESS; 251 } 252 253 WARN_(I8042PRT, "Got 0x%02x instead of 0xFA\n", Value); 254 return STATUS_IO_DEVICE_ERROR; 255 } 256 257 static VOID NTAPI 258 i8042MouDpcRoutine( 259 IN PKDPC Dpc, 260 IN PVOID DeferredContext, 261 IN PVOID SystemArgument1, 262 IN PVOID SystemArgument2) 263 { 264 PI8042_MOUSE_EXTENSION DeviceExtension; 265 PPORT_DEVICE_EXTENSION PortDeviceExtension; 266 ULONG MouseTransferred = 0; 267 ULONG MouseInBufferCopy; 268 KIRQL Irql; 269 LARGE_INTEGER Timeout; 270 271 UNREFERENCED_PARAMETER(Dpc); 272 UNREFERENCED_PARAMETER(SystemArgument1); 273 UNREFERENCED_PARAMETER(SystemArgument2); 274 275 __analysis_assume(DeferredContext != NULL); 276 DeviceExtension = DeferredContext; 277 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 278 279 switch (DeviceExtension->MouseTimeoutState) 280 { 281 case TimeoutStart: 282 { 283 DeviceExtension->MouseTimeoutState = NoChange; 284 if (DeviceExtension->MouseTimeoutActive && 285 !KeCancelTimer(&DeviceExtension->TimerMouseTimeout)) 286 { 287 /* The timer fired already, give up */ 288 DeviceExtension->MouseTimeoutActive = FALSE; 289 return; 290 } 291 292 Timeout.QuadPart = -15000000; /* 1.5 seconds, should be enough */ 293 294 KeSetTimer( 295 &DeviceExtension->TimerMouseTimeout, 296 Timeout, 297 &DeviceExtension->DpcMouseTimeout); 298 DeviceExtension->MouseTimeoutActive = TRUE; 299 return; 300 } 301 302 case TimeoutCancel: 303 { 304 DeviceExtension->MouseTimeoutState = NoChange; 305 KeCancelTimer(&DeviceExtension->TimerMouseTimeout); 306 DeviceExtension->MouseTimeoutActive = FALSE; 307 } 308 309 default: 310 ;/* nothing, don't want a warning */ 311 } 312 313 /* Should be unlikely */ 314 if (!DeviceExtension->MouseComplete) 315 return; 316 317 Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); 318 319 DeviceExtension->MouseComplete = FALSE; 320 MouseInBufferCopy = DeviceExtension->MouseInBuffer; 321 322 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); 323 324 TRACE_(I8042PRT, "Send a mouse packet\n"); 325 326 if (!DeviceExtension->MouseData.ClassService) 327 return; 328 329 INFO_(I8042PRT, "Sending %lu mouse move(s)\n", MouseInBufferCopy); 330 (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->MouseData.ClassService)( 331 DeviceExtension->MouseData.ClassDeviceObject, 332 DeviceExtension->MouseBuffer, 333 DeviceExtension->MouseBuffer + MouseInBufferCopy, 334 &MouseTransferred); 335 336 Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); 337 DeviceExtension->MouseInBuffer -= MouseTransferred; 338 if (DeviceExtension->MouseInBuffer) 339 RtlMoveMemory( 340 DeviceExtension->MouseBuffer, 341 DeviceExtension->MouseBuffer + MouseTransferred, 342 DeviceExtension->MouseInBuffer * sizeof(MOUSE_INPUT_DATA)); 343 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); 344 } 345 346 /* This timer DPC will be called when the mouse reset times out. 347 * I'll just send the 'disable mouse port' command to the controller 348 * and say the mouse doesn't exist. 349 */ 350 static VOID NTAPI 351 i8042DpcRoutineMouseTimeout( 352 IN PKDPC Dpc, 353 IN PVOID DeferredContext, 354 IN PVOID SystemArgument1, 355 IN PVOID SystemArgument2) 356 { 357 PI8042_MOUSE_EXTENSION DeviceExtension; 358 PPORT_DEVICE_EXTENSION PortDeviceExtension; 359 KIRQL Irql; 360 361 UNREFERENCED_PARAMETER(Dpc); 362 UNREFERENCED_PARAMETER(SystemArgument1); 363 UNREFERENCED_PARAMETER(SystemArgument2); 364 365 __analysis_assume(DeferredContext != NULL); 366 DeviceExtension = DeferredContext; 367 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 368 369 Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); 370 371 WARN_(I8042PRT, "Mouse initialization timeout! (substate %x)\n", 372 DeviceExtension->MouseResetState); 373 374 PortDeviceExtension->Flags &= ~MOUSE_PRESENT; 375 376 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); 377 } 378 379 /* 380 * Runs the mouse IOCTL_INTERNAL dispatch. 381 */ 382 NTSTATUS NTAPI 383 i8042MouInternalDeviceControl( 384 IN PDEVICE_OBJECT DeviceObject, 385 IN PIRP Irp) 386 { 387 PIO_STACK_LOCATION Stack; 388 PI8042_MOUSE_EXTENSION DeviceExtension; 389 NTSTATUS Status; 390 391 Stack = IoGetCurrentIrpStackLocation(Irp); 392 Irp->IoStatus.Information = 0; 393 DeviceExtension = (PI8042_MOUSE_EXTENSION)DeviceObject->DeviceExtension; 394 395 switch (Stack->Parameters.DeviceIoControl.IoControlCode) 396 { 397 case IOCTL_INTERNAL_MOUSE_CONNECT: 398 { 399 SIZE_T Size; 400 PIO_WORKITEM WorkItem = NULL; 401 PI8042_HOOK_WORKITEM WorkItemData = NULL; 402 403 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_CONNECT\n"); 404 if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA)) 405 { 406 Status = STATUS_INVALID_PARAMETER; 407 goto cleanup; 408 } 409 410 DeviceExtension->MouseData = 411 *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer); 412 413 /* Send IOCTL_INTERNAL_I8042_HOOK_MOUSE to device stack */ 414 WorkItem = IoAllocateWorkItem(DeviceObject); 415 if (!WorkItem) 416 { 417 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n"); 418 Status = STATUS_INSUFFICIENT_RESOURCES; 419 goto cleanup; 420 } 421 WorkItemData = ExAllocatePoolWithTag( 422 NonPagedPool, 423 sizeof(I8042_HOOK_WORKITEM), 424 I8042PRT_TAG); 425 if (!WorkItemData) 426 { 427 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n"); 428 Status = STATUS_NO_MEMORY; 429 goto cleanup; 430 } 431 WorkItemData->WorkItem = WorkItem; 432 WorkItemData->Irp = Irp; 433 434 /* Initialize extension */ 435 DeviceExtension->Common.Type = Mouse; 436 Size = DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize * sizeof(MOUSE_INPUT_DATA); 437 DeviceExtension->MouseBuffer = ExAllocatePoolWithTag( 438 NonPagedPool, 439 Size, 440 I8042PRT_TAG); 441 if (!DeviceExtension->MouseBuffer) 442 { 443 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n"); 444 Status = STATUS_NO_MEMORY; 445 goto cleanup; 446 } 447 RtlZeroMemory(DeviceExtension->MouseBuffer, Size); 448 DeviceExtension->MouseAttributes.InputDataQueueLength = 449 DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize; 450 KeInitializeDpc( 451 &DeviceExtension->DpcMouse, 452 i8042MouDpcRoutine, 453 DeviceExtension); 454 KeInitializeDpc( 455 &DeviceExtension->DpcMouseTimeout, 456 i8042DpcRoutineMouseTimeout, 457 DeviceExtension); 458 KeInitializeTimer(&DeviceExtension->TimerMouseTimeout); 459 DeviceExtension->Common.PortDeviceExtension->MouseExtension = DeviceExtension; 460 DeviceExtension->Common.PortDeviceExtension->Flags |= MOUSE_CONNECTED; 461 462 IoMarkIrpPending(Irp); 463 DeviceExtension->MouseState = MouseResetting; 464 DeviceExtension->MouseResetState = ExpectingReset; 465 DeviceExtension->MouseHook.IsrWritePort = i8042MouIsrWritePort; 466 DeviceExtension->MouseHook.QueueMousePacket = i8042MouQueuePacket; 467 DeviceExtension->MouseHook.CallContext = DeviceExtension; 468 IoQueueWorkItem(WorkItem, 469 i8042SendHookWorkItem, 470 DelayedWorkQueue, 471 WorkItemData); 472 Status = STATUS_PENDING; 473 break; 474 475 cleanup: 476 if (DeviceExtension->MouseBuffer) 477 ExFreePoolWithTag(DeviceExtension->MouseBuffer, I8042PRT_TAG); 478 if (WorkItem) 479 IoFreeWorkItem(WorkItem); 480 if (WorkItemData) 481 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); 482 break; 483 } 484 case IOCTL_INTERNAL_MOUSE_DISCONNECT: 485 { 486 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_DISCONNECT\n"); 487 /* MSDN says that operation is to implemented. 488 * To implement it, we just have to do: 489 * DeviceExtension->MouseData.ClassService = NULL; 490 */ 491 Status = STATUS_NOT_IMPLEMENTED; 492 break; 493 } 494 case IOCTL_INTERNAL_I8042_HOOK_MOUSE: 495 { 496 PINTERNAL_I8042_HOOK_MOUSE MouseHook; 497 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_MOUSE\n"); 498 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(INTERNAL_I8042_HOOK_MOUSE)) 499 { 500 Status = STATUS_INVALID_PARAMETER; 501 break; 502 } 503 MouseHook = (PINTERNAL_I8042_HOOK_MOUSE)Stack->Parameters.DeviceIoControl.Type3InputBuffer; 504 505 DeviceExtension->MouseHook.Context = MouseHook->Context; 506 if (MouseHook->IsrRoutine) 507 DeviceExtension->MouseHook.IsrRoutine = MouseHook->IsrRoutine; 508 509 Status = STATUS_SUCCESS; 510 break; 511 } 512 case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER: 513 { 514 DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER not implemented\n"); 515 Status = STATUS_NOT_IMPLEMENTED; 516 break; 517 } 518 case IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION: 519 { 520 DPRINT1("IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION not implemented\n"); 521 Status = STATUS_NOT_IMPLEMENTED; 522 break; 523 } 524 case IOCTL_MOUSE_QUERY_ATTRIBUTES: 525 { 526 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_MOUSE_QUERY_ATTRIBUTES\n"); 527 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES)) 528 { 529 Status = STATUS_BUFFER_TOO_SMALL; 530 break; 531 } 532 533 *(PMOUSE_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer = DeviceExtension->MouseAttributes; 534 Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES); 535 Status = STATUS_SUCCESS; 536 break; 537 } 538 default: 539 { 540 ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n", 541 Stack->Parameters.DeviceIoControl.IoControlCode); 542 ASSERT(FALSE); 543 return ForwardIrpAndForget(DeviceObject, Irp); 544 } 545 } 546 547 if (Status != STATUS_PENDING) 548 { 549 Irp->IoStatus.Status = Status; 550 IoCompleteRequest(Irp, IO_NO_INCREMENT); 551 } 552 return Status; 553 } 554 555 /* Test if packets are taking too long to come in. If they do, we 556 * might have gotten out of sync and should just drop what we have. 557 * 558 * If we want to be totally right, we'd also have to keep a count of 559 * errors, and totally reset the mouse after too much of them (can 560 * happen if the user is using a KVM switch and an OS on another port 561 * resets the mouse, or if the user hotplugs the mouse, or if we're just 562 * generally unlucky). Also note the input parsing routine where we 563 * drop invalid input packets. 564 */ 565 static VOID 566 i8042MouInputTestTimeout( 567 IN PI8042_MOUSE_EXTENSION DeviceExtension) 568 { 569 ULARGE_INTEGER Now; 570 571 if (DeviceExtension->MouseState == MouseExpectingACK || 572 DeviceExtension->MouseState == MouseResetting) 573 return; 574 575 Now.QuadPart = KeQueryInterruptTime(); 576 577 if (DeviceExtension->MouseState != MouseIdle) { 578 /* Check if the last byte came too long ago */ 579 if (Now.QuadPart - DeviceExtension->MousePacketStartTime.QuadPart > 580 DeviceExtension->Common.PortDeviceExtension->Settings.MouseSynchIn100ns) 581 { 582 WARN_(I8042PRT, "Mouse input packet timeout\n"); 583 DeviceExtension->MouseState = MouseIdle; 584 } 585 } 586 587 if (DeviceExtension->MouseState == MouseIdle) 588 DeviceExtension->MousePacketStartTime.QuadPart = Now.QuadPart; 589 } 590 591 /* 592 * Call the customization hook. The ToReturn parameter is about wether 593 * we should go on with the interrupt. The return value is what 594 * we should return (indicating to the system wether someone else 595 * should try to handle the interrupt) 596 */ 597 static BOOLEAN 598 i8042MouCallIsrHook( 599 IN PI8042_MOUSE_EXTENSION DeviceExtension, 600 IN UCHAR Status, 601 IN UCHAR Input, 602 OUT PBOOLEAN ToReturn) 603 { 604 BOOLEAN HookReturn, HookContinue; 605 606 HookContinue = FALSE; 607 608 if (DeviceExtension->MouseHook.IsrRoutine) 609 { 610 HookReturn = DeviceExtension->MouseHook.IsrRoutine( 611 DeviceExtension->MouseHook.Context, 612 DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer, 613 &DeviceExtension->Common.PortDeviceExtension->Packet, 614 Status, 615 &Input, 616 &HookContinue, 617 &DeviceExtension->MouseState, 618 &DeviceExtension->MouseResetState); 619 620 if (!HookContinue) 621 { 622 *ToReturn = HookReturn; 623 return TRUE; 624 } 625 } 626 return FALSE; 627 } 628 629 static BOOLEAN 630 i8042MouResetIsr( 631 IN PI8042_MOUSE_EXTENSION DeviceExtension, 632 IN UCHAR Status, 633 IN UCHAR Value) 634 { 635 PPORT_DEVICE_EXTENSION PortDeviceExtension; 636 BOOLEAN ToReturn = FALSE; 637 638 if (i8042MouCallIsrHook(DeviceExtension, Status, Value, &ToReturn)) 639 return ToReturn; 640 641 if (MouseIdle == DeviceExtension->MouseState) 642 { 643 /* Magic packet value that indicates a reset */ 644 if (0xAA == Value) 645 { 646 WARN_(I8042PRT, "Hot plugged mouse!\n"); 647 DeviceExtension->MouseState = MouseResetting; 648 DeviceExtension->MouseResetState = ExpectingReset; 649 } 650 else 651 return FALSE; 652 } 653 else if (MouseResetting != DeviceExtension->MouseState) 654 return FALSE; 655 656 DeviceExtension->MouseTimeoutState = TimeoutStart; 657 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 658 659 switch ((ULONG)DeviceExtension->MouseResetState) 660 { 661 case ExpectingReset: 662 if (MOUSE_ACK == Value) 663 { 664 WARN_(I8042PRT, "Dropping extra ACK\n"); 665 return TRUE; 666 } 667 668 /* First, 0xFF is sent. The mouse is supposed to say AA00 if ok, FC00 if not. */ 669 if (0xAA == Value) 670 { 671 DeviceExtension->MouseResetState++; 672 } 673 else 674 { 675 PortDeviceExtension->Flags &= ~MOUSE_PRESENT; 676 DeviceExtension->MouseState = MouseIdle; 677 WARN_(I8042PRT, "Mouse returned bad reset reply: %x (expected aa)\n", Value); 678 } 679 return TRUE; 680 case ExpectingResetId: 681 if (MOUSE_ACK == Value) 682 { 683 WARN_(I8042PRT, "Dropping extra ACK #2\n"); 684 return TRUE; 685 } 686 687 if (0x00 == Value) 688 { 689 DeviceExtension->MouseResetState++; 690 DeviceExtension->MouseType = GenericPS2; 691 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2); 692 } 693 else 694 { 695 PortDeviceExtension->Flags &= ~MOUSE_PRESENT; 696 DeviceExtension->MouseState = MouseIdle; 697 WARN_(I8042PRT, "Mouse returned bad reset reply part two: %x (expected 0)\n", Value); 698 } 699 return TRUE; 700 case ExpectingGetDeviceIdACK: 701 if (MOUSE_ACK == Value) 702 { 703 DeviceExtension->MouseResetState++; 704 } 705 else if (MOUSE_NACK == Value || MOUSE_ERROR == Value) 706 { 707 DeviceExtension->MouseResetState++; 708 /* Act as if 00 (normal mouse) was received */ 709 WARN_(I8042PRT, "Mouse doesn't support 0xd2, (returns %x, expected %x), faking\n", Value, MOUSE_ACK); 710 i8042MouResetIsr(DeviceExtension, Status, 0); 711 } 712 return TRUE; 713 case ExpectingGetDeviceIdValue: 714 switch (Value) 715 { 716 case 0x02: 717 DeviceExtension->MouseAttributes.MouseIdentifier = 718 BALLPOINT_I8042_HARDWARE; 719 break; 720 case 0x03: 721 case 0x04: 722 DeviceExtension->MouseAttributes.MouseIdentifier = 723 WHEELMOUSE_I8042_HARDWARE; 724 break; 725 default: 726 DeviceExtension->MouseAttributes.MouseIdentifier = 727 MOUSE_I8042_HARDWARE; 728 } 729 DeviceExtension->MouseResetState++; 730 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8); 731 return TRUE; 732 case ExpectingSetResolutionDefaultACK: 733 DeviceExtension->MouseResetState++; 734 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x00); 735 return TRUE; 736 case ExpectingSetResolutionDefaultValueACK: 737 DeviceExtension->MouseResetState = ExpectingSetScaling1to1ACK; 738 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6); 739 return TRUE; 740 case ExpectingSetScaling1to1ACK: 741 case ExpectingSetScaling1to1ACK2: 742 DeviceExtension->MouseResetState++; 743 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6); 744 return TRUE; 745 case ExpectingSetScaling1to1ACK3: 746 DeviceExtension->MouseResetState++; 747 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE9); 748 return TRUE; 749 case ExpectingReadMouseStatusACK: 750 DeviceExtension->MouseResetState++; 751 return TRUE; 752 case ExpectingReadMouseStatusByte1: 753 DeviceExtension->MouseLogiBuffer[0] = Value; 754 DeviceExtension->MouseResetState++; 755 return TRUE; 756 case ExpectingReadMouseStatusByte2: 757 DeviceExtension->MouseLogiBuffer[1] = Value; 758 DeviceExtension->MouseResetState++; 759 return TRUE; 760 case ExpectingReadMouseStatusByte3: 761 DeviceExtension->MouseLogiBuffer[2] = Value; 762 /* Now MouseLogiBuffer is a set of info. If the second 763 * byte is 0, the mouse didn't understand the magic 764 * code. Otherwise, it it a Logitech and the second byte 765 * is the number of buttons, bit 7 of the first byte tells 766 * if it understands special E7 commands, the rest is an ID. 767 */ 768 if (DeviceExtension->MouseLogiBuffer[1]) 769 { 770 DeviceExtension->MouseAttributes.NumberOfButtons = 771 DeviceExtension->MouseLogiBuffer[1]; 772 DeviceExtension->MouseType = Ps2pp; 773 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); 774 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK; 775 /* TODO: Go through EnableWheel and Enable5Buttons */ 776 return TRUE; 777 } 778 DeviceExtension->MouseResetState = EnableWheel; 779 i8042MouResetIsr(DeviceExtension, Status, Value); 780 return TRUE; 781 case EnableWheel: 782 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); 783 DeviceExtension->MouseResetState = 1001; 784 return TRUE; 785 case 1001: 786 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8); 787 DeviceExtension->MouseResetState++; 788 return TRUE; 789 case 1002: 790 case 1004: 791 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); 792 DeviceExtension->MouseResetState++; 793 return TRUE; 794 case 1003: 795 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x64); 796 DeviceExtension->MouseResetState++; 797 return TRUE; 798 case 1005: 799 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50); 800 DeviceExtension->MouseResetState++; 801 return TRUE; 802 case 1006: 803 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2); 804 DeviceExtension->MouseResetState++; 805 return TRUE; 806 case 1007: 807 /* Ignore ACK */ 808 DeviceExtension->MouseResetState++; 809 return TRUE; 810 case 1008: 811 if (0x03 == Value) { 812 /* It's either an Intellimouse or Intellimouse Explorer. */ 813 DeviceExtension->MouseAttributes.NumberOfButtons = 3; 814 DeviceExtension->MouseAttributes.MouseIdentifier = 815 WHEELMOUSE_I8042_HARDWARE; 816 DeviceExtension->MouseType = Intellimouse; 817 DeviceExtension->MouseResetState = Enable5Buttons; 818 i8042MouResetIsr(DeviceExtension, Status, Value); 819 } 820 else 821 { 822 /* Just set the default settings and be done */ 823 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); 824 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK; 825 } 826 return TRUE; 827 case Enable5Buttons: 828 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); 829 DeviceExtension->MouseResetState = 1021; 830 return TRUE; 831 case 1022: 832 case 1024: 833 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); 834 DeviceExtension->MouseResetState++; 835 return TRUE; 836 case 1021: 837 case 1023: 838 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8); 839 DeviceExtension->MouseResetState++; 840 return TRUE; 841 case 1025: 842 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50); 843 DeviceExtension->MouseResetState++; 844 return TRUE; 845 case 1026: 846 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2); 847 DeviceExtension->MouseResetState++; 848 return TRUE; 849 case 1027: 850 if (0x04 == Value) 851 { 852 DeviceExtension->MouseAttributes.NumberOfButtons = 5; 853 DeviceExtension->MouseAttributes.MouseIdentifier = 854 WHEELMOUSE_I8042_HARDWARE; 855 DeviceExtension->MouseType = IntellimouseExplorer; 856 } 857 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); 858 DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK; 859 return TRUE; 860 case ExpectingSetSamplingRateACK: 861 DeviceExtension->MouseHook.IsrWritePort( 862 DeviceExtension->MouseHook.CallContext, 863 (UCHAR)DeviceExtension->MouseAttributes.SampleRate); 864 DeviceExtension->MouseResetState++; 865 return TRUE; 866 case ExpectingSetSamplingRateValueACK: 867 if (MOUSE_NACK == Value) 868 { 869 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x3C); 870 DeviceExtension->MouseAttributes.SampleRate = (USHORT)PortDeviceExtension->Settings.SampleRate; 871 DeviceExtension->MouseResetState = 1040; 872 return TRUE; 873 } 874 case 1040: /* Fallthrough */ 875 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8); 876 DeviceExtension->MouseResetState = ExpectingFinalResolutionACK; 877 return TRUE; 878 case ExpectingFinalResolutionACK: 879 DeviceExtension->MouseHook.IsrWritePort( 880 DeviceExtension->MouseHook.CallContext, 881 (UCHAR)(PortDeviceExtension->Settings.MouseResolution & 0xff)); 882 INFO_(I8042PRT, "Mouse resolution %lu\n", 883 PortDeviceExtension->Settings.MouseResolution); 884 DeviceExtension->MouseResetState = ExpectingFinalResolutionValueACK; 885 return TRUE; 886 case ExpectingFinalResolutionValueACK: 887 DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF4); 888 DeviceExtension->MouseResetState = ExpectingEnableACK; 889 return TRUE; 890 case ExpectingEnableACK: 891 PortDeviceExtension->Flags |= MOUSE_PRESENT; 892 DeviceExtension->MouseState = MouseIdle; 893 DeviceExtension->MouseTimeoutState = TimeoutCancel; 894 INFO_(I8042PRT, "Mouse type = %u\n", DeviceExtension->MouseType); 895 return TRUE; 896 default: 897 if (DeviceExtension->MouseResetState < 100 || DeviceExtension->MouseResetState > 999) 898 ERR_(I8042PRT, "MouseResetState went out of range: %lu\n", DeviceExtension->MouseResetState); 899 return FALSE; 900 } 901 } 902 903 BOOLEAN NTAPI 904 i8042MouInterruptService( 905 IN PKINTERRUPT Interrupt, 906 PVOID Context) 907 { 908 PI8042_MOUSE_EXTENSION DeviceExtension; 909 PPORT_DEVICE_EXTENSION PortDeviceExtension; 910 ULONG Counter; 911 UCHAR Output = 0, PortStatus = 0; 912 NTSTATUS Status; 913 914 UNREFERENCED_PARAMETER(Interrupt); 915 916 __analysis_assume(Context != NULL); 917 DeviceExtension = Context; 918 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 919 Counter = PortDeviceExtension->Settings.PollStatusIterations; 920 921 while (Counter) 922 { 923 Status = i8042ReadStatus(PortDeviceExtension, &PortStatus); 924 if (!NT_SUCCESS(Status)) 925 { 926 WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status); 927 return FALSE; 928 } 929 Status = i8042ReadMouseData(PortDeviceExtension, &Output); 930 if (NT_SUCCESS(Status)) 931 break; 932 KeStallExecutionProcessor(1); 933 Counter--; 934 } 935 if (Counter == 0) 936 { 937 WARN_(I8042PRT, "Spurious i8042 mouse interrupt\n"); 938 return FALSE; 939 } 940 941 INFO_(I8042PRT, "Got: 0x%02x\n", Output); 942 943 if (i8042PacketIsr(PortDeviceExtension, Output)) 944 { 945 if (PortDeviceExtension->PacketComplete) 946 { 947 TRACE_(I8042PRT, "Packet complete\n"); 948 KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL); 949 } 950 TRACE_(I8042PRT, "Irq eaten by packet\n"); 951 return TRUE; 952 } 953 954 TRACE_(I8042PRT, "Irq is mouse input\n"); 955 956 i8042MouInputTestTimeout(DeviceExtension); 957 958 if (i8042MouResetIsr(DeviceExtension, PortStatus, Output)) 959 { 960 TRACE_(I8042PRT, "Handled by ResetIsr or hooked Isr\n"); 961 if (NoChange != DeviceExtension->MouseTimeoutState) { 962 KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL); 963 } 964 return TRUE; 965 } 966 967 if (DeviceExtension->MouseType == Ps2pp) 968 i8042MouHandlePs2pp(DeviceExtension, Output); 969 else 970 i8042MouHandle(DeviceExtension, Output); 971 972 return TRUE; 973 } 974