📄 mouse.c
字号:
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/mouse.c
* PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
* mouse specifics
* PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
* Jason Filby (jasonfilby@yahoo.com)
* Tinus
*/
/* INCLUDES ****************************************************************/
#include "i8042prt.h"
#define NDEBUG
#include <debug.h>
/*
* These functions are callbacks for filter driver custom interrupt
* service routines.
*/
static VOID STDCALL I8042IsrWritePortMouse(PVOID Context,
UCHAR Value)
{
I8042IsrWritePort(Context, Value, 0xD4);
}
#if 0
static NTSTATUS STDCALL I8042SynchWritePortMouse(PVOID Context,
UCHAR Value,
BOOLEAN WaitForAck)
{
return I8042SynchWritePort((PDEVICE_EXTENSION)Context,
0xD4,
Value,
WaitForAck);
}
#endif
/* Test if packets are taking too long to come in. If they do, we
* might have gotten out of sync and should just drop what we have.
*
* If we want to be totally right, we'd also have to keep a count of
* errors, and totally reset the mouse after too much of them (can
* happen if the user is using a KVM switch and an OS on another port
* resets the mouse, or if the user hotplugs the mouse, or if we're just
* generally unlucky). Also note the input parsing routine where we
* drop invalid input packets.
*/
static VOID STDCALL I8042MouseInputTestTimeout(PDEVICE_EXTENSION DevExt)
{
ULARGE_INTEGER Now;
if (DevExt->MouseState == MouseExpectingACK ||
DevExt->MouseState == MouseResetting)
return;
Now.QuadPart = KeQueryInterruptTime();
if (DevExt->MouseState != MouseIdle) {
/* Check if the last byte came too long ago */
if (Now.QuadPart - DevExt->MousePacketStartTime.QuadPart >
DevExt->Settings.MouseSynchIn100ns) {
DPRINT1("Mouse input packet timeout\n");
DevExt->MouseState = MouseIdle;
}
}
if (DevExt->MouseState == MouseIdle)
DevExt->MousePacketStartTime.QuadPart = Now.QuadPart;
}
/*
* Call the customization hook. The Ret2 parameter is about wether
* we should go on with the interrupt. The return value is what
* we should return (indicating to the system wether someone else
* should try to handle the interrupt)
*/
static BOOLEAN STDCALL I8042MouseCallIsrHook(PDEVICE_EXTENSION DevExt,
UCHAR Status,
PUCHAR Input,
PBOOLEAN ToReturn)
{
BOOLEAN HookReturn, HookContinue;
HookContinue = FALSE;
if (DevExt->MouseHook.IsrRoutine) {
HookReturn = DevExt->MouseHook.IsrRoutine(
DevExt->MouseHook.Context,
DevExt->MouseBuffer + DevExt->MouseInBuffer,
&DevExt->Packet,
Status,
Input,
&HookContinue,
&DevExt->MouseState,
&DevExt->MouseResetState);
if (!HookContinue) {
*ToReturn = HookReturn;
return TRUE;
}
}
return FALSE;
}
static BOOLEAN STDCALL I8042MouseResetIsr(PDEVICE_EXTENSION DevExt,
UCHAR Status,
PUCHAR Value)
{
BOOLEAN ToReturn = FALSE;
if (I8042MouseCallIsrHook(DevExt, Status, Value, &ToReturn))
return ToReturn;
if (MouseResetting != DevExt->MouseState) {
return FALSE;
}
DevExt->MouseTimeoutState = TimeoutStart;
switch (DevExt->MouseResetState) {
case 1100: /* the first ack, drop it. */
DevExt->MouseResetState = ExpectingReset;
return TRUE;
/* First, 0xFF is sent. The mouse is supposed to say AA00 if ok,
* FC00 if not.
*/
case ExpectingReset:
if (0xAA == *Value) {
DevExt->MouseResetState++;
} else {
DevExt->MouseExists = FALSE;
DevExt->MouseState = MouseIdle;
DPRINT("Mouse returned bad reset reply: "
"%x (expected aa)\n", *Value);
}
return TRUE;
case ExpectingResetId:
if (0x00 == *Value) {
DevExt->MouseResetState++;
DevExt->MouseType = GenericPS2;
I8042IsrWritePortMouse(DevExt, 0xF2);
} else {
DevExt->MouseExists = FALSE;
DevExt->MouseState = MouseIdle;
DPRINT1("Mouse returned bad reset reply part two: "
"%x (expected 0)\n", *Value);
}
return TRUE;
case ExpectingGetDeviceIdACK:
if (MOUSE_ACK == *Value) {
DevExt->MouseResetState++;
} else if (MOUSE_NACK == *Value ||
MOUSE_ERROR == *Value) {
DevExt->MouseResetState++;
/* Act as if 00 (normal mouse) was received */
DPRINT("Mouse doesn't support 0xd2, "
"(returns %x, expected %x), faking.\n",
*Value, MOUSE_ACK);
*Value = 0;
I8042MouseResetIsr(DevExt, Status, Value);
}
return TRUE;
case ExpectingGetDeviceIdValue:
switch (*Value) {
case 0x02:
DevExt->MouseAttributes.MouseIdentifier =
BALLPOINT_I8042_HARDWARE;
break;
case 0x03:
case 0x04:
DevExt->MouseAttributes.MouseIdentifier =
WHEELMOUSE_I8042_HARDWARE;
break;
default:
DevExt->MouseAttributes.MouseIdentifier =
MOUSE_I8042_HARDWARE;
}
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0xE8);
return TRUE;
case ExpectingSetResolutionDefaultACK:
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0x00);
return TRUE;
case ExpectingSetResolutionDefaultValueACK:
DevExt->MouseResetState = ExpectingSetScaling1to1ACK;
I8042IsrWritePortMouse(DevExt, 0xE6);
return TRUE;
case ExpectingSetScaling1to1ACK:
case ExpectingSetScaling1to1ACK2:
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0xE6);
return TRUE;
case ExpectingSetScaling1to1ACK3:
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0xE9);
return TRUE;
case ExpectingReadMouseStatusACK:
DevExt->MouseResetState++;
return TRUE;
case ExpectingReadMouseStatusByte1:
DevExt->MouseLogiBuffer[0] = *Value;
DevExt->MouseResetState++;
return TRUE;
case ExpectingReadMouseStatusByte2:
DevExt->MouseLogiBuffer[1] = *Value;
DevExt->MouseResetState++;
return TRUE;
case ExpectingReadMouseStatusByte3:
DevExt->MouseLogiBuffer[2] = *Value;
/* Now MouseLogiBuffer is a set of info. If the second
* byte is 0, the mouse didn't understand the magic
* code. Otherwise, it it a Logitech and the second byte
* is the number of buttons, bit 7 of the first byte tells
* if it understands special E7 commands, the rest is an ID.
*/
if (DevExt->MouseLogiBuffer[1]) {
DevExt->MouseAttributes.NumberOfButtons =
DevExt->MouseLogiBuffer[1];
/* For some reason the ID is the wrong way around */
DevExt->MouseLogitechID =
((DevExt->MouseLogiBuffer[0] >> 4) & 0x07) |
((DevExt->MouseLogiBuffer[0] << 3) & 0x78);
DevExt->MouseType = Ps2pp;
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = ExpectingSetSamplingRateACK;
return TRUE;
/* TODO: Go through EnableWheel and Enable5Buttons */
}
DevExt->MouseResetState = EnableWheel;
I8042MouseResetIsr(DevExt, Status, Value);
return TRUE;
case EnableWheel:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = 1001;
return TRUE;
case 1001:
I8042IsrWritePortMouse(DevExt, 0xc8);
DevExt->MouseResetState++;
return TRUE;
case 1002:
case 1004:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState++;
return TRUE;
case 1003:
I8042IsrWritePortMouse(DevExt, 0x64);
DevExt->MouseResetState++;
return TRUE;
case 1005:
I8042IsrWritePortMouse(DevExt, 0x50);
DevExt->MouseResetState++;
return TRUE;
case 1006:
I8042IsrWritePortMouse(DevExt, 0xf2);
DevExt->MouseResetState++;
return TRUE;
case 1007:
/* Ignore ACK */
DevExt->MouseResetState++;
return TRUE;
case 1008:
/* Now if the value == 3, it's either an Intellimouse
* or Intellimouse Explorer. */
if (0x03 == *Value) {
DevExt->MouseAttributes.NumberOfButtons = 3;
DevExt->MouseAttributes.MouseIdentifier =
WHEELMOUSE_I8042_HARDWARE;
DevExt->MouseType = Intellimouse;
DevExt->MouseResetState = Enable5Buttons;
I8042MouseResetIsr(DevExt, Status, Value);
return TRUE;
} /* Else, just set the default settings and be done */
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = ExpectingSetSamplingRateACK;
return TRUE;
case Enable5Buttons:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = 1021;
return TRUE;
case 1022:
case 1024:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState++;
return TRUE;
case 1021:
case 1023:
I8042IsrWritePortMouse(DevExt, 0xc8);
DevExt->MouseResetState++;
return TRUE;
case 1025:
I8042IsrWritePortMouse(DevExt, 0x50);
DevExt->MouseResetState++;
return TRUE;
case 1026:
I8042IsrWritePortMouse(DevExt, 0xf2);
DevExt->MouseResetState++;
return TRUE;
case 1027:
if (0x04 == *Value) {
DevExt->MouseAttributes.NumberOfButtons = 5;
DevExt->MouseAttributes.MouseIdentifier =
WHEELMOUSE_I8042_HARDWARE;
DevExt->MouseType = IntellimouseExplorer;
}
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = ExpectingSetSamplingRateACK;
return TRUE;
case ExpectingSetSamplingRateACK:
I8042IsrWritePortMouse(DevExt,
(UCHAR)DevExt->MouseAttributes.SampleRate);
DevExt->MouseResetState++;
return TRUE;
case ExpectingSetSamplingRateValueACK:
if (MOUSE_NACK == *Value) {
I8042IsrWritePortMouse(DevExt, 0x3c);
DevExt->MouseAttributes.SampleRate = 60;
DevExt->MouseResetState = 1040;
return TRUE;
}
case 1040: /* Fallthrough */
I8042IsrWritePortMouse(DevExt, 0xe8);
DevExt->MouseResetState = ExpectingFinalResolutionACK;
return TRUE;
case ExpectingFinalResolutionACK:
I8042IsrWritePortMouse(DevExt,
(UCHAR)(DevExt->Settings.MouseResolution & 0xff));
DPRINT("%x\n", DevExt->Settings.MouseResolution);
DevExt->MouseResetState = ExpectingFinalResolutionValueACK;
return TRUE;
case ExpectingFinalResolutionValueACK:
I8042IsrWritePortMouse(DevExt, 0xf4);
DevExt->MouseResetState = ExpectingEnableACK;
return TRUE;
case ExpectingEnableACK:
DevExt->MouseState = MouseIdle;
DevExt->MouseTimeoutState = TimeoutCancel;
DPRINT("Mouse type = %d\n", DevExt->MouseType);
return TRUE;
default:
if (DevExt->MouseResetState < 100 ||
DevExt->MouseResetState > 999)
DPRINT1("MouseResetState went out of range: %d\n",
DevExt->MouseResetState);
return FALSE;
}
}
/*
* Prepare for reading the next packet and queue the dpc for handling
* this one.
*
* Context is the device object.
*/
VOID STDCALL I8042QueueMousePacket(PVOID Context)
{
PDEVICE_OBJECT DeviceObject = Context;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
DevExt->MouseComplete = TRUE;
DevExt->MouseInBuffer++;
if (DevExt->MouseInBuffer >
DevExt->MouseAttributes.InputDataQueueLength) {
DPRINT1("Mouse buffer overflow\n");
DevExt->MouseInBuffer--;
}
DPRINT("Irq completes mouse packet\n");
KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL);
}
/*
* Updates ButtonFlags according to RawButtons and a saved state;
* Only takes in account the bits that are set in Mask
*/
VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt,
USHORT Mask)
{
PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
DevExt->MouseInBuffer;
USHORT NewButtonData = (USHORT)(MouseInput->RawButtons & Mask);
USHORT ButtonDiff = (NewButtonData ^ DevExt->MouseButtonState) & Mask;
/* Note that the defines are such:
* MOUSE_LEFT_BUTTON_DOWN 1
* MOUSE_LEFT_BUTTON_UP 2
*/
MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) |
(((~(NewButtonData)) << 1) &
(ButtonDiff << 1)) |
(MouseInput->RawButtons & 0xfc00);
DPRINT("Left raw/up/down: %d/%d/%d\n", MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN,
MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN,
MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP);
DevExt->MouseButtonState = (DevExt->MouseButtonState & ~Mask) |
(NewButtonData & Mask);
}
VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt,
UCHAR Output)
{
PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
DevExt->MouseInBuffer;
CHAR Scroll;
switch (DevExt->MouseState) {
case MouseIdle:
/* This bit should be 1, if not drop the packet, we
* might be lucky and get in sync again
*/
if (!(Output & 8)) {
DPRINT1("Bad input, dropping..\n");
return;
}
MouseInput->Buttons = 0;
MouseInput->RawButtons = 0;
MouseInput->Flags = MOUSE_MOVE_RELATIVE;
/* Note how we ignore the overflow bits, like Windows
* is said to do. There's no reasonable thing to do
* anyway.
*/
if (Output & 16)
MouseInput->LastX = 1;
else
MouseInput->LastX = 0;
if (Output & 32)
MouseInput->LastY = 1;
else
MouseInput->LastY = 0;
if (Output & 1) {
MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN;
}
if (Output & 2) {
MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN;
}
if (Output & 4) {
MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN;
}
DevExt->MouseState = XMovement;
break;
case XMovement:
if (MouseInput->LastX)
MouseInput->LastX = (LONG) Output - 256;
else
MouseInput->LastX = Output;
DevExt->MouseState = YMovement;
break;
case YMovement:
if (MouseInput->LastY)
MouseInput->LastY = (LONG)Output - 256;
else
MouseInput->LastY = (LONG)Output;
/* Windows wants it the other way around */
MouseInput->LastY = -MouseInput->LastY;
if (DevExt->MouseType == GenericPS2 ||
DevExt->MouseType == Ps2pp) {
I8042MouseHandleButtons(DevExt,
MOUSE_LEFT_BUTTON_DOWN |
MOUSE_RIGHT_BUTTON_DOWN |
MOUSE_MIDDLE_BUTTON_DOWN);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -