mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4716 lines
151 KiB
4716 lines
151 KiB
/*++
|
|
|
|
Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved
|
|
|
|
Module Name:
|
|
|
|
moudep.c
|
|
|
|
Abstract:
|
|
|
|
The initialization and hardware-dependent portions of
|
|
the Intel i8042 port driver which are specific to
|
|
the auxiliary (PS/2 mouse) device.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
NOTES: (Future/outstanding issues)
|
|
|
|
- Powerfail not implemented.
|
|
|
|
- Consolidate duplicate code, where possible and appropriate.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
#include "string.h"
|
|
#include "i8042prt.h"
|
|
#include "i8042log.h"
|
|
|
|
//
|
|
// Use the alloc_text pragma to specify the driver initialization routines
|
|
// (they can be paged out).
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, I8xMouseConfiguration)
|
|
#pragma alloc_text(PAGE, I8xMouseServiceParameters)
|
|
#pragma alloc_text(PAGE, I8xInitializeMouse)
|
|
#pragma alloc_text(PAGE, I8xGetBytePolledIterated)
|
|
#pragma alloc_text(PAGE, I8xTransmitByteSequence)
|
|
#pragma alloc_text(PAGE, I8xFindWheelMouse)
|
|
#pragma alloc_text(PAGE, I8xQueryNumberOfMouseButtons)
|
|
#pragma alloc_text(PAGE, MouseCopyWheelIDs)
|
|
|
|
//
|
|
// These will be locked down right before the mouse interrupt is enabled if a
|
|
// mouse is present
|
|
//
|
|
#pragma alloc_text(PAGEMOUC, I8042MouseInterruptService)
|
|
#pragma alloc_text(PAGEMOUC, I8xQueueCurrentMouseInput)
|
|
#pragma alloc_text(PAGEMOUC, I8xVerifyMousePnPID)
|
|
#endif
|
|
|
|
#define ONE_PAST_FINAL_SAMPLE ((UCHAR) 0x00)
|
|
|
|
static const
|
|
UCHAR PnpDetectCommands[] = { 20,
|
|
40,
|
|
60,
|
|
ONE_PAST_FINAL_SAMPLE
|
|
};
|
|
|
|
static const
|
|
UCHAR WheelEnableCommands[] = { 200,
|
|
100,
|
|
80,
|
|
ONE_PAST_FINAL_SAMPLE
|
|
};
|
|
|
|
static const
|
|
UCHAR FiveButtonEnableCommands[] = { 200,
|
|
200,
|
|
80,
|
|
ONE_PAST_FINAL_SAMPLE
|
|
};
|
|
|
|
#if MOUSE_RECORD_ISR
|
|
|
|
PMOUSE_STATE_RECORD IsrStateHistory = NULL;
|
|
PMOUSE_STATE_RECORD CurrentIsrState = NULL;
|
|
PMOUSE_STATE_RECORD IsrStateHistoryEnd = NULL;
|
|
|
|
#endif // MOUSE_RECORD_ISR
|
|
|
|
#define BUFFER_FULL (OUTPUT_BUFFER_FULL | MOUSE_OUTPUT_BUFFER_FULL)
|
|
|
|
#define RML_BUTTONS (RIGHT_BUTTON | MIDDLE_BUTTON | LEFT_BUTTON)
|
|
#define BUTTONS_4_5 (BUTTON_4 | BUTTON_5)
|
|
|
|
#define _TRANSITION_DOWN(previous, current, button) \
|
|
((!(previous & button)) && (current & button))
|
|
|
|
#define _TRANSITION_UP(previous, current, button) \
|
|
((previous & button) && (!(current & button)))
|
|
|
|
BOOLEAN
|
|
I8042MouseInterruptService(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the actual work. It either processes a mouse packet
|
|
or the results from a write to the device.
|
|
|
|
Arguments:
|
|
|
|
CallIsrContext - Contains the interrupt object and device object.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the interrupt was truly ours
|
|
|
|
--*/
|
|
{
|
|
PPORT_MOUSE_EXTENSION deviceExtension;
|
|
LARGE_INTEGER tickDelta, newTick;
|
|
UCHAR previousButtons;
|
|
UCHAR previousSignAndOverflow;
|
|
UCHAR byte, statusByte, lastByte;
|
|
UCHAR resendCommand, nextCommand, altCommand;
|
|
BOOLEAN bSendCommand, ret = TRUE;
|
|
static PWCHAR currentIdChar;
|
|
|
|
#define TRANSITION_UP(button) _TRANSITION_UP(previousButtons, byte, button)
|
|
#define TRANSITION_DOWN(button) _TRANSITION_DOWN(previousButtons, byte, button)
|
|
|
|
IsrPrint(DBG_MOUISR_TRACE, ("%s\n", pEnter));
|
|
|
|
deviceExtension = (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
if (deviceExtension->PowerState != PowerDeviceD0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify that this device really interrupted. Check the status
|
|
// register. The Output Buffer Full bit should be set, and the
|
|
// Auxiliary Device Output Buffer Full bit should be set.
|
|
//
|
|
statusByte =
|
|
I8X_GET_STATUS_BYTE(Globals.ControllerData->DeviceRegisters[CommandPort]);
|
|
if ((statusByte & BUFFER_FULL) != BUFFER_FULL) {
|
|
//
|
|
// Stall and then try again. The Olivetti MIPS machine
|
|
// sometimes gets a mouse interrupt before the status
|
|
// register is set.
|
|
//
|
|
KeStallExecutionProcessor(10);
|
|
statusByte = I8X_GET_STATUS_BYTE(Globals.ControllerData->DeviceRegisters[CommandPort]);
|
|
if ((statusByte & BUFFER_FULL) != BUFFER_FULL) {
|
|
|
|
//
|
|
// Not our interrupt.
|
|
//
|
|
IsrPrint(DBG_MOUISR_ERROR | DBG_MOUISR_INFO,
|
|
("not our interrupt!\n"
|
|
));
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read the byte from the i8042 data port.
|
|
//
|
|
I8xGetByteAsynchronous(
|
|
(CCHAR) MouseDeviceType,
|
|
&byte
|
|
);
|
|
|
|
IsrPrint(DBG_MOUISR_BYTE, ("byte 0x%x\n", byte));
|
|
|
|
KeQueryTickCount(&newTick);
|
|
|
|
if (deviceExtension->InputResetSubState == QueueingMouseReset) {
|
|
|
|
RECORD_ISR_STATE(deviceExtension,
|
|
byte,
|
|
deviceExtension->LastByteReceived,
|
|
newTick);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (deviceExtension->InputState == MouseResetting && byte == FAILURE) {
|
|
RECORD_ISR_STATE(deviceExtension,
|
|
byte,
|
|
deviceExtension->LastByteReceived,
|
|
newTick);
|
|
deviceExtension->LastByteReceived = byte;
|
|
ret = TRUE;
|
|
goto IsrResetMouse;
|
|
}
|
|
|
|
if (deviceExtension->IsrHookCallback) {
|
|
BOOLEAN cont= FALSE;
|
|
|
|
ret = (*deviceExtension->IsrHookCallback)(
|
|
deviceExtension->HookContext,
|
|
&deviceExtension->CurrentInput,
|
|
&deviceExtension->CurrentOutput,
|
|
statusByte,
|
|
&byte,
|
|
&cont,
|
|
&deviceExtension->InputState,
|
|
&deviceExtension->InputResetSubState
|
|
);
|
|
|
|
if (!cont) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Watch the data stream for a reset completion (0xaa) followed by the
|
|
// device id
|
|
//
|
|
// this pattern can appear as part of a normal data packet as well. This
|
|
// code assumes that sending an enable to an already enabled mouse will:
|
|
// * not hang the mouse
|
|
// * abort the current packet and return an ACK.
|
|
//
|
|
|
|
if (deviceExtension->LastByteReceived == MOUSE_COMPLETE &&
|
|
(byte == 0x00 || byte == 0x03)) {
|
|
|
|
IsrPrint(DBG_MOUISR_RESETTING, ("received id %2d\n", byte));
|
|
|
|
RECORD_ISR_STATE(deviceExtension,
|
|
byte,
|
|
deviceExtension->LastByteReceived,
|
|
newTick);
|
|
|
|
if (InterlockedCompareExchangePointer(&deviceExtension->ResetIrp,
|
|
NULL,
|
|
NULL) == NULL) {
|
|
//
|
|
// user unplugged and plugged in the mouse...queue a reset packet
|
|
// so the programming of the mouse in the ISR does not conflict with
|
|
// any other writes to the i8042prt controller
|
|
//
|
|
IsrPrint(DBG_MOUISR_RESETTING, ("user initiated reset...queueing\n"));
|
|
goto IsrResetMouseOnly;
|
|
}
|
|
|
|
//
|
|
// Tell the 8042 port to fetch the device ID of the aux device
|
|
// We do this async so that we don't spin at IRQ1!!!
|
|
//
|
|
I8X_WRITE_CMD_TO_MOUSE();
|
|
I8X_MOUSE_COMMAND( GET_DEVICE_ID );
|
|
RECORD_ISR_STATE_COMMAND(deviceExtension, GET_DEVICE_ID);
|
|
|
|
//
|
|
// This is part of the substate system for handling a (possible)
|
|
// mouse reset.
|
|
//
|
|
deviceExtension->InputState = MouseResetting;
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceIdACK;
|
|
|
|
//
|
|
// We don't want to execute any more of the ISR code, so lets just
|
|
// do a few things and then return now
|
|
//
|
|
deviceExtension->LastByteReceived = byte;
|
|
deviceExtension->ResendCount = 0;
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
if (deviceExtension->InputState == MouseIdle &&
|
|
deviceExtension->CurrentOutput.State != Idle &&
|
|
DeviceObject->CurrentIrp != NULL) {
|
|
if (byte == RESEND) {
|
|
|
|
//
|
|
// If the timer count is zero, don't process the interrupt
|
|
// further. The timeout routine will complete this request.
|
|
//
|
|
if (Globals.ControllerData->TimerCount == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Reset the timeout value to indicate no timeout.
|
|
//
|
|
Globals.ControllerData->TimerCount = I8042_ASYNC_NO_TIMEOUT;
|
|
|
|
if (deviceExtension->ResendCount <
|
|
Globals.ControllerData->Configuration.ResendIterations) {
|
|
|
|
//
|
|
// retard the byte count to resend the last byte
|
|
//
|
|
deviceExtension->CurrentOutput.CurrentByte -= 1;
|
|
deviceExtension->ResendCount += 1;
|
|
I8xInitiateIo(DeviceObject);
|
|
|
|
} else {
|
|
|
|
deviceExtension->CurrentOutput.State = Idle;
|
|
|
|
KeInsertQueueDpc(&deviceExtension->RetriesExceededDpc,
|
|
DeviceObject,
|
|
NULL
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (byte == ACKNOWLEDGE) {
|
|
//
|
|
// The keyboard controller has acknowledged a previous send.
|
|
// If there are more bytes to send for the current packet, initiate
|
|
// the next send operation. Otherwise, queue the completion DPC.
|
|
//
|
|
// If the timer count is zero, don't process the interrupt
|
|
// further. The timeout routine will complete this request.
|
|
//
|
|
if (Globals.ControllerData->TimerCount == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Reset the timeout value to indicate no timeout.
|
|
//
|
|
Globals.ControllerData->TimerCount = I8042_ASYNC_NO_TIMEOUT;
|
|
|
|
//
|
|
// Reset resend count.
|
|
//
|
|
deviceExtension->ResendCount = 0;
|
|
if (deviceExtension->CurrentOutput.CurrentByte <
|
|
deviceExtension->CurrentOutput.ByteCount) {
|
|
//
|
|
// We've successfully sent the first byte of a 2-byte
|
|
// command sequence. Initiate a send of the second byte.
|
|
//
|
|
IsrPrint(DBG_MOUISR_STATE,
|
|
("now initiate send of byte #%d\n",
|
|
deviceExtension->CurrentOutput.CurrentByte
|
|
));
|
|
|
|
I8xInitiateIo(DeviceObject);
|
|
}
|
|
else {
|
|
//
|
|
// We've successfully sent all bytes in the command sequence.
|
|
// Reset the current state and queue the completion DPC.
|
|
//
|
|
|
|
IsrPrint(DBG_MOUISR_STATE,
|
|
("all bytes have been sent\n"
|
|
));
|
|
|
|
deviceExtension->CurrentOutput.State = Idle;
|
|
|
|
ASSERT(DeviceObject->CurrentIrp != NULL);
|
|
|
|
IoRequestDpc(
|
|
DeviceObject,
|
|
DeviceObject->CurrentIrp,
|
|
IntToPtr(IsrDpcCauseMouseWriteComplete)
|
|
);
|
|
}
|
|
|
|
//
|
|
// No matter what, we are done processing for now
|
|
//
|
|
return TRUE;
|
|
}
|
|
else {
|
|
//
|
|
// do what here, eh?
|
|
//
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember what the last byte we got was
|
|
//
|
|
lastByte = deviceExtension->LastByteReceived;
|
|
deviceExtension->LastByteReceived = byte;
|
|
|
|
//
|
|
// Take the appropriate action, depending on the current state.
|
|
// When the state is Idle, we expect to receive mouse button
|
|
// data. When the state is XMovement, we expect to receive mouse
|
|
// motion in the X direction data. When the state is YMovement,
|
|
// we expect to receive mouse motion in the Y direction data. Once
|
|
// the Y motion data has been received, the data is queued to the
|
|
// mouse InputData queue, the mouse ISR DPC is requested, and the
|
|
// state returns to Idle.
|
|
//
|
|
tickDelta.QuadPart =
|
|
newTick.QuadPart -
|
|
deviceExtension->PreviousTick.QuadPart;
|
|
|
|
if ((deviceExtension->InputState != MouseIdle)
|
|
&& (deviceExtension->InputState != MouseExpectingACK)
|
|
&& (deviceExtension->InputState != MouseResetting)
|
|
&& ((tickDelta.LowPart >= deviceExtension->SynchTickCount)
|
|
|| (tickDelta.HighPart != 0))) {
|
|
|
|
//
|
|
// It has been a long time since we got a byte of
|
|
// the data packet. Assume that we are now receiving
|
|
// the first byte of a new packet, and discard any
|
|
// partially received packet.
|
|
//
|
|
// N.B. We assume that SynchTickCount is ULONG, and avoid
|
|
// a LARGE_INTEGER compare with tickDelta...
|
|
//
|
|
|
|
IsrPrint(DBG_MOUISR_STATE,
|
|
("State was %d, synching\n",
|
|
deviceExtension->InputState
|
|
));
|
|
|
|
//
|
|
// The device misbehaved. Lets play it safe and reset the device.
|
|
//
|
|
// Note: this code is meant to handle cases where some intermediate
|
|
// (switch) box resets the mouse and doesn't tell us about it.
|
|
// This avoid problems with trying to detect this code since there
|
|
// isn't a fool proof way to do it
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
|
|
deviceExtension->PreviousTick = newTick;
|
|
|
|
switch(deviceExtension->InputState) {
|
|
|
|
//
|
|
// The mouse interrupted with a status byte. The status byte
|
|
// contains information on the mouse button state along with
|
|
// the sign and overflow bits for the (yet-to-be-received)
|
|
// X and Y motion bytes.
|
|
//
|
|
|
|
case MouseIdle: {
|
|
|
|
IsrPrint(DBG_MOUISR_STATE, ("mouse status byte\n"));
|
|
|
|
//
|
|
// This is a sanity check test. It is required because some people
|
|
// in industry persist in their notion that you can reset a mouse
|
|
// device any time you want and not let the OS know anything about
|
|
// it. This results in our nice little wheel mouse (which is a
|
|
// 4 byte packet engine) suddenly only dumping 3 byte packets.
|
|
//
|
|
if (WHEEL_PRESENT() && (byte & 0xC8) != 8 ) { // Guaranteed True for Megallan
|
|
|
|
//
|
|
// We are getting a bad packet for the idle state. The best bet
|
|
// is to issue a mouse reset request and hope we can recover.
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
|
|
//
|
|
// Update CurrentInput with button transition data.
|
|
// I.e., set a button up/down bit in the Buttons field if
|
|
// the state of a given button has changed since we
|
|
// received the last packet.
|
|
//
|
|
|
|
previousButtons = deviceExtension->PreviousButtons;
|
|
|
|
// This clears both ButtonFlags and ButtonData
|
|
deviceExtension->CurrentInput.Buttons = 0;
|
|
deviceExtension->CurrentInput.Flags = 0x0;
|
|
|
|
if (TRANSITION_DOWN(LEFT_BUTTON)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_LEFT_BUTTON_DOWN;
|
|
} else
|
|
if (TRANSITION_UP(LEFT_BUTTON)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_LEFT_BUTTON_UP;
|
|
}
|
|
if (TRANSITION_DOWN(RIGHT_BUTTON)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_RIGHT_BUTTON_DOWN;
|
|
} else
|
|
if (TRANSITION_UP(RIGHT_BUTTON)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_RIGHT_BUTTON_UP;
|
|
}
|
|
if (TRANSITION_DOWN(MIDDLE_BUTTON)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_MIDDLE_BUTTON_DOWN;
|
|
} else
|
|
if (TRANSITION_UP(MIDDLE_BUTTON)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_MIDDLE_BUTTON_UP;
|
|
}
|
|
|
|
//
|
|
// Save the button state for comparison the next time around.
|
|
// (previousButtons will never have 4/5 set if a 5 button mouse is
|
|
// not present, but checking to see if a 5 button mouse is present
|
|
// is just as expensive as the additional & and | so just do it with
|
|
// out regard to the 5 button mouse's presence
|
|
//
|
|
deviceExtension->PreviousButtons =
|
|
(byte & RML_BUTTONS) | (previousButtons & BUTTONS_4_5);
|
|
|
|
//
|
|
// Save the sign and overflow information from the current byte.
|
|
//
|
|
deviceExtension->CurrentSignAndOverflow =
|
|
(UCHAR) (byte & MOUSE_SIGN_OVERFLOW_MASK);
|
|
|
|
//
|
|
// Update to the next state.
|
|
//
|
|
deviceExtension->InputState = XMovement;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The mouse interrupted with the X motion byte. Apply
|
|
// the sign and overflow bits from the mouse status byte received
|
|
// previously. Attempt to correct for bogus changes in sign
|
|
// that occur with large, rapid mouse movements.
|
|
//
|
|
|
|
case XMovement: {
|
|
|
|
IsrPrint(DBG_MOUISR_STATE, ("mouse LastX byte\n"));
|
|
|
|
//
|
|
// Update CurrentInput with the X motion data.
|
|
//
|
|
|
|
if (deviceExtension->CurrentSignAndOverflow
|
|
& X_OVERFLOW) {
|
|
|
|
//
|
|
// Handle overflow in the X direction. If the previous
|
|
// mouse movement overflowed too, ensure that the current
|
|
// overflow is in the same direction (i.e., that the sign
|
|
// is the same as it was for the previous event). We do this
|
|
// to correct for hardware problems -- it should not be possible
|
|
// to overflow in one direction and then immediately overflow
|
|
// in the opposite direction.
|
|
//
|
|
|
|
previousSignAndOverflow =
|
|
deviceExtension->PreviousSignAndOverflow;
|
|
if (previousSignAndOverflow & X_OVERFLOW) {
|
|
if ((previousSignAndOverflow & X_DATA_SIGN) !=
|
|
(deviceExtension->CurrentSignAndOverflow
|
|
& X_DATA_SIGN)) {
|
|
deviceExtension->CurrentSignAndOverflow
|
|
^= X_DATA_SIGN;
|
|
}
|
|
}
|
|
|
|
if (deviceExtension->CurrentSignAndOverflow &
|
|
X_DATA_SIGN)
|
|
deviceExtension->CurrentInput.LastX =
|
|
(LONG) MOUSE_MAXIMUM_NEGATIVE_DELTA;
|
|
else
|
|
deviceExtension->CurrentInput.LastX =
|
|
(LONG) MOUSE_MAXIMUM_POSITIVE_DELTA;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No overflow. Just store the data, correcting for the
|
|
// sign if necessary.
|
|
//
|
|
|
|
deviceExtension->CurrentInput.LastX = (ULONG) byte;
|
|
if (deviceExtension->CurrentSignAndOverflow &
|
|
X_DATA_SIGN)
|
|
deviceExtension->CurrentInput.LastX |=
|
|
MOUSE_MAXIMUM_NEGATIVE_DELTA;
|
|
}
|
|
|
|
//
|
|
// Update to the next state.
|
|
//
|
|
|
|
deviceExtension->InputState = YMovement;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The mouse interrupted with the Y motion byte. Apply
|
|
// the sign and overflow bits from the mouse status byte received
|
|
// previously. [Attempt to correct for bogus changes in sign
|
|
// that occur with large, rapid mouse movements.] Write the
|
|
// data to the mouse InputData queue, and queue the mouse ISR DPC
|
|
// to complete the interrupt processing.
|
|
//
|
|
|
|
case YMovement: {
|
|
|
|
IsrPrint(DBG_MOUISR_STATE, ("mouse LastY byte\n"));
|
|
|
|
//
|
|
// Update CurrentInput with the Y motion data.
|
|
//
|
|
|
|
if (deviceExtension->CurrentSignAndOverflow
|
|
& Y_OVERFLOW) {
|
|
|
|
//
|
|
// Handle overflow in the Y direction. If the previous
|
|
// mouse movement overflowed too, ensure that the current
|
|
// overflow is in the same direction (i.e., that the sign
|
|
// is the same as it was for the previous event). We do this
|
|
// to correct for hardware problems -- it should not be possible
|
|
// to overflow in one direction and then immediately overflow
|
|
// in the opposite direction.
|
|
//
|
|
|
|
previousSignAndOverflow =
|
|
deviceExtension->PreviousSignAndOverflow;
|
|
if (previousSignAndOverflow & Y_OVERFLOW) {
|
|
if ((previousSignAndOverflow & Y_DATA_SIGN) !=
|
|
(deviceExtension->CurrentSignAndOverflow
|
|
& Y_DATA_SIGN)) {
|
|
deviceExtension->CurrentSignAndOverflow
|
|
^= Y_DATA_SIGN;
|
|
}
|
|
}
|
|
|
|
if (deviceExtension->CurrentSignAndOverflow &
|
|
Y_DATA_SIGN)
|
|
deviceExtension->CurrentInput.LastY =
|
|
(LONG) MOUSE_MAXIMUM_POSITIVE_DELTA;
|
|
else
|
|
deviceExtension->CurrentInput.LastY =
|
|
(LONG) MOUSE_MAXIMUM_NEGATIVE_DELTA;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No overflow. Just store the data, correcting for the
|
|
// sign if necessary.
|
|
//
|
|
|
|
deviceExtension->CurrentInput.LastY = (ULONG) byte;
|
|
if (deviceExtension->CurrentSignAndOverflow &
|
|
Y_DATA_SIGN)
|
|
deviceExtension->CurrentInput.LastY |=
|
|
MOUSE_MAXIMUM_NEGATIVE_DELTA;
|
|
|
|
//
|
|
// Negate the LastY value (the hardware reports positive
|
|
// motion in the direction that we consider negative).
|
|
//
|
|
|
|
deviceExtension->CurrentInput.LastY =
|
|
-deviceExtension->CurrentInput.LastY;
|
|
|
|
}
|
|
|
|
//
|
|
// Update our notion of the previous sign and overflow bits for
|
|
// the start of the next mouse input sequence.
|
|
//
|
|
|
|
deviceExtension->PreviousSignAndOverflow =
|
|
deviceExtension->CurrentSignAndOverflow;
|
|
|
|
//
|
|
// Choose the next state. The WheelMouse has an extra byte of data
|
|
// for us
|
|
//
|
|
|
|
if (WHEEL_PRESENT()) {
|
|
deviceExtension->InputState = ZMovement;
|
|
}
|
|
else {
|
|
I8xQueueCurrentMouseInput(DeviceObject);
|
|
deviceExtension->InputState = MouseIdle;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ZMovement: {
|
|
|
|
IsrPrint(DBG_MOUISR_STATE, ("mouse LastZ byte\n"));
|
|
|
|
//
|
|
// This code is here to handle the cases were mouse resets were
|
|
// not notified to the OS. Uncomment this if you *really* want it,
|
|
// but remember that it could possibly reset the mouse when it
|
|
// shouldn't have...
|
|
//
|
|
#if 0
|
|
if ( (byte & 0xf8) != 0 && (byte & 0xf8) != 0xf8 ) {
|
|
|
|
//
|
|
// for some reason, the byte was not sign extanded,
|
|
// or has a value > 7, which we assume cannot be
|
|
// possible giving our equipment. So the packet
|
|
// *must* be bogus...
|
|
//
|
|
// No longer the case with 5 button mice
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
#endif
|
|
//
|
|
// Check to see if we got any z data
|
|
// If there were any changes in the button state, ignore the
|
|
// z data
|
|
//
|
|
if (FIVE_PRESENT()) {
|
|
//
|
|
// Wheel info first, value returned should be
|
|
// -120 * the value reported
|
|
//
|
|
if (byte & 0x0F) {
|
|
|
|
// sign extend to the upper 4 bits if necessary
|
|
if (byte & 0x08) {
|
|
deviceExtension->CurrentInput.ButtonData =
|
|
(-120) * ((CHAR)((byte & 0xF) | 0xF0));
|
|
}
|
|
else {
|
|
deviceExtension->CurrentInput.ButtonData =
|
|
(-120) * ((CHAR) byte & 0xF);
|
|
}
|
|
|
|
deviceExtension->CurrentInput.ButtonFlags |= MOUSE_WHEEL;
|
|
}
|
|
|
|
previousButtons = deviceExtension->PreviousButtons;
|
|
|
|
// extra buttons
|
|
if (TRANSITION_DOWN(BUTTON_4)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_BUTTON_4_DOWN;
|
|
} else
|
|
if (TRANSITION_UP(BUTTON_4)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_BUTTON_4_UP;
|
|
}
|
|
if (TRANSITION_DOWN(BUTTON_5)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_BUTTON_5_DOWN;
|
|
} else
|
|
if (TRANSITION_UP(BUTTON_5)) {
|
|
deviceExtension->CurrentInput.ButtonFlags |=
|
|
MOUSE_BUTTON_5_UP;
|
|
}
|
|
|
|
// record btns 4 & 5 w/out losing btns 1-3
|
|
deviceExtension->PreviousButtons =
|
|
(byte & BUTTONS_4_5) | (previousButtons & RML_BUTTONS);
|
|
}
|
|
else if (byte) {
|
|
deviceExtension->CurrentInput.ButtonData =
|
|
(-120) * ((CHAR) byte);
|
|
|
|
deviceExtension->CurrentInput.ButtonFlags |= MOUSE_WHEEL;
|
|
}
|
|
|
|
//
|
|
// Pack the data on to the class driver
|
|
//
|
|
|
|
I8xQueueCurrentMouseInput(DeviceObject);
|
|
|
|
//
|
|
// Reset the state
|
|
//
|
|
|
|
deviceExtension->InputState = MouseIdle;
|
|
|
|
break;
|
|
}
|
|
|
|
case MouseExpectingACK: {
|
|
|
|
RECORD_ISR_STATE(deviceExtension, byte, lastByte, newTick);
|
|
|
|
//
|
|
// This is a special case. We hit this on one of the very
|
|
// first mouse interrupts following the IoConnectInterrupt.
|
|
// The interrupt is caused when we enable mouse transmissions
|
|
// via I8xMouseEnableTransmission() -- the hardware returns
|
|
// an ACK. Just toss this byte away, and set the input state
|
|
// to coincide with the start of a new mouse data packet.
|
|
//
|
|
|
|
IsrPrint(DBG_MOUISR_BYTE,
|
|
("...should be from I8xMouseEnableTransmission\n"));
|
|
IsrPrint(DBG_MOUISR_BYTE,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
|
|
deviceExtension->InputState = MouseIdle;
|
|
deviceExtension->EnableMouse.Enabled = FALSE;
|
|
|
|
} else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// Resend the "Enable Mouse Transmission" sequence.
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = ENABLE_MOUSE_TRANSMISSION;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MouseResetting: {
|
|
|
|
IsrPrint(DBG_MOUISR_RESETTING,
|
|
("state (%d) substate (%2d)\n",
|
|
deviceExtension->InputState,
|
|
deviceExtension->InputResetSubState
|
|
));
|
|
|
|
//
|
|
// We enter the reset substate machine
|
|
//
|
|
SwitchOnInputResetSubState:
|
|
bSendCommand = TRUE;
|
|
altCommand = (UCHAR) 0x00;
|
|
RECORD_ISR_STATE(deviceExtension, byte, lastByte, newTick);
|
|
switch (deviceExtension->InputResetSubState) {
|
|
|
|
case StartPnPIdDetection:
|
|
ASSERT(byte == (UCHAR) ACKNOWLEDGE);
|
|
nextCommand = SET_MOUSE_SAMPLING_RATE;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingLoopSetSamplingRateACK;
|
|
deviceExtension->SampleRatesIndex = 0;
|
|
deviceExtension->SampleRates = (PUCHAR) PnpDetectCommands;
|
|
deviceExtension->PostSamplesState = ExpectingPnpId;
|
|
break;
|
|
|
|
case EnableWheel:
|
|
bSendCommand = FALSE;
|
|
altCommand = SET_MOUSE_SAMPLING_RATE;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingLoopSetSamplingRateACK;
|
|
deviceExtension->SampleRatesIndex = 0;
|
|
deviceExtension->SampleRates = (PUCHAR) WheelEnableCommands;
|
|
|
|
//
|
|
// After enabling the wheel, we shall get the device ID because
|
|
// some kinds of wheel mice require the get id right after the
|
|
// wheel enabling sequence
|
|
//
|
|
deviceExtension->PostSamplesState = PostEnableWheelState;
|
|
break;
|
|
|
|
case PostEnableWheelState:
|
|
//
|
|
// Some wheel mice require a get device ID after turning on the
|
|
// wheel for the wheel to be truly turned on
|
|
//
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceIdDetectACK;
|
|
break;
|
|
|
|
case ExpectingGetDeviceIdDetectACK:
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
bSendCommand = FALSE;
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceIdDetectValue;
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = GET_DEVICE_ID;
|
|
}
|
|
else {
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_GET_DEVICE_ID_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case ExpectingGetDeviceIdDetectValue:
|
|
//
|
|
// In theory, we can check the mouse ID here and only send the
|
|
// 5 button enable sequence if the mouse ID is the wheel mouse
|
|
// ID....BUT there are filter drivers which trap the ISR and
|
|
// depend on the mouse ID always showing up in the
|
|
// ExpectingGetDeviceId2Value state
|
|
//
|
|
deviceExtension->InputResetSubState = Enable5Buttons;
|
|
goto SwitchOnInputResetSubState;
|
|
// break;
|
|
|
|
case Enable5Buttons:
|
|
bSendCommand = FALSE;
|
|
altCommand = SET_MOUSE_SAMPLING_RATE;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingLoopSetSamplingRateACK;
|
|
deviceExtension->SampleRatesIndex = 0;
|
|
deviceExtension->SampleRates = (PUCHAR) FiveButtonEnableCommands;
|
|
deviceExtension->PostSamplesState = PostWheelDetectState;
|
|
break;
|
|
|
|
//
|
|
// This state (ExpectingReset) and the next one (Expecting
|
|
// ResetId) are only called if we have to issue a reset
|
|
// from within the substate machine.
|
|
//
|
|
case ExpectingReset:
|
|
//
|
|
// This case handles 3 cases
|
|
//
|
|
// 1) The ack resulting from writing a MOUSE_RESET (0xFF)
|
|
// 2) A resend resulting from writing the reset
|
|
// 3) The reset character following the the ack
|
|
//
|
|
// If the byte is neither of these 3, then just let it go
|
|
//
|
|
if (byte == ACKNOWLEDGE) {
|
|
//
|
|
// The ack for the reset, the 0xAA will come immediately
|
|
// after this. We can handle it in the same state
|
|
//
|
|
bSendCommand = FALSE;
|
|
break;
|
|
|
|
}
|
|
else if (byte == RESEND) {
|
|
|
|
bSendCommand = FALSE;
|
|
if (deviceExtension->ResendCount++ < MOUSE_RESET_RESENDS_MAX &&
|
|
|
|
deviceExtension->ResetMouse.IsrResetState
|
|
== IsrResetNormal) {
|
|
|
|
IsrPrint(DBG_MOUISR_RESETTING, ("resending from isr\n"));
|
|
|
|
//
|
|
// Fix for old Digital machines (both x86 and alpha)
|
|
// that can't handle resets to close together
|
|
//
|
|
KeStallExecutionProcessor(
|
|
deviceExtension->MouseResetStallTime
|
|
);
|
|
|
|
//
|
|
// We send an alt command instead of the normal
|
|
// resetCommand so that we can maintain our own count
|
|
// here (we want the reset resend max to be larger than
|
|
// the std resend max)
|
|
//
|
|
altCommand = MOUSE_RESET;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if (byte != MOUSE_COMPLETE) {
|
|
//
|
|
// Check to see if we got a reset character (0xAA). If
|
|
// not, then we will *ignore* it. Note that this means
|
|
// that if we dropped this char, then we could infinite
|
|
// loop in this routine.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now check to see how many times we have gone
|
|
// through this routine without exiting the
|
|
// MouseResetting SubState
|
|
//
|
|
if (deviceExtension->ResetCount >= MOUSE_RESETS_MAX) {
|
|
//
|
|
// This will queue a reset DPC which will see that too many
|
|
// resets have been sent and will clean up the counters and
|
|
// and start the next Irp in the StartIO queue
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
|
|
//
|
|
// Because of the test for mouse resets at the start of
|
|
// the ISR, the following code should really have no
|
|
// effect
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingResetId;
|
|
|
|
break;
|
|
|
|
//
|
|
// This state is special in that its just here as a place
|
|
// holder because we have a detection routine at the start
|
|
// of the ISR that automically puts us into
|
|
// ExpectingGetDeviceIdACK.
|
|
//
|
|
// OLD WAY:
|
|
// For completeness though, we
|
|
// issue a bugcheck here since we can't normally get into
|
|
// this state
|
|
//
|
|
// NEW WAY:
|
|
// We ignore this state entirely. As far as I can tell, we get
|
|
// into this state when the controller requests a resend (which
|
|
// is honored) and then sends the 0xFA, 0xAA in reverse order,
|
|
// which the state machine handles, but hits this assert
|
|
//
|
|
case ExpectingResetId:
|
|
//
|
|
// Next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceIdACK;
|
|
|
|
if (byte == WHEELMOUSE_ID_BYTE) {
|
|
|
|
//
|
|
// Start a enable command to the device. We *really* don't
|
|
// expect to be here.
|
|
//
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
}
|
|
else {
|
|
#if 0
|
|
//
|
|
// Log that we are in a bad state
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
(PVOID) (ULONG) I8042_INVALID_ISR_STATE_MOU
|
|
);
|
|
|
|
ASSERT( byte == WHEELMOUSE_ID_BYTE);
|
|
#endif
|
|
//
|
|
// For sake of completeness
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingReset;
|
|
|
|
}
|
|
break;
|
|
|
|
case ExpectingGetDeviceIdACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingIdAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceIdValue;
|
|
|
|
bSendCommand = FALSE;
|
|
|
|
} else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// Resend the "Get Mouse ID Transmission" sequence.
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = GET_DEVICE_ID;
|
|
} else {
|
|
|
|
//
|
|
// If we got here, then we don't know what's going
|
|
// on with the device... The best bet is to send down
|
|
// a mouse reset command...
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
|
|
break;
|
|
|
|
case ExpectingGetDeviceIdValue:
|
|
|
|
IsrPrint(DBG_MOUISR_RESETTING,
|
|
("id from get device id is %2d\n" ,
|
|
(ULONG) byte
|
|
));
|
|
|
|
//
|
|
// Got the device ID from the mouse and compare it with what we
|
|
// expect to see. If the ID byte is still wheel or five button
|
|
// then we STILL cannot consider this to be data from the mouse
|
|
// that has mirrored a reset. There are two reasons why we can't
|
|
// consider this real data:
|
|
//
|
|
// 1) Switch boxes cache the device ID and returned the cached
|
|
// ID upon a reset
|
|
// 2) Some mice, once put in the 4 byte packet mode, will return
|
|
// the wheel or 5 button ID byte even if they have been reset
|
|
//
|
|
// Furthermore, we cannot check to see if extension->ResetIrp
|
|
// exists because this does not cover the case where the mouse
|
|
// was unplugged / replugged in.
|
|
//
|
|
// @@BEGIN_DDKSPLIT
|
|
// This is a pit of despair. No matter how crafty we try to be,
|
|
// we will never get this right! Perhaps we can turn on this
|
|
// check via a reg flag. The MS hardware folks have
|
|
// respectfully requested this many, many times and we have
|
|
// tried to accomodate them, but we always get bitten by the fix
|
|
// in the end.
|
|
// @@END_DDKSPLIT
|
|
|
|
// (byte != WHEELMOUSE_ID_BYTE) && byte != (FIVEBUTTON_ID_BYTE)) {
|
|
if (1) {
|
|
|
|
//
|
|
// Log an error/warning message so that we can track these
|
|
// problems down in the field
|
|
//
|
|
|
|
#if 0
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
(PVOID)(ULONG) (WHEEL_PRESENT() ?
|
|
I8042_UNEXPECTED_WHEEL_MOUSE_RESET :
|
|
I8042_UNEXPECTED_MOUSE_RESET)
|
|
);
|
|
#endif
|
|
bSendCommand = FALSE;
|
|
if (deviceExtension->NumberOfButtonsOverride != 0) {
|
|
//
|
|
// skip button detection and set the res
|
|
//
|
|
altCommand = POST_BUTTONDETECT_COMMAND;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
POST_BUTTONDETECT_COMMAND_SUBSTATE;
|
|
}
|
|
else {
|
|
altCommand = SET_MOUSE_RESOLUTION;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetResolutionACK;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// We have a wheel mouse present here... Log something so that
|
|
// we know that we got here.
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_BOGUS_MOUSE_RESET)
|
|
);
|
|
|
|
|
|
//
|
|
// Lets go back to the idle state
|
|
//
|
|
deviceExtension->InputState = MouseIdle;
|
|
|
|
//
|
|
// Reset the number of possible mouse resets
|
|
//
|
|
deviceExtension->ResetCount = 0;
|
|
|
|
}
|
|
break;
|
|
|
|
case ExpectingSetResolutionACK:
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingIdAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
|
|
//
|
|
// Set the resolution to 0x00
|
|
//
|
|
nextCommand = 0x00;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetResolutionValueACK;
|
|
|
|
} else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = SET_MOUSE_RESOLUTION;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_RESOLUTION_FAILED)
|
|
);
|
|
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2ACK;
|
|
|
|
}
|
|
break;
|
|
|
|
case ExpectingSetResolutionValueACK:
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingIdAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
|
|
nextCommand = SET_MOUSE_SCALING_1TO1;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetScaling1to1ACK;
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = 0x00;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_SAMPLE_RATE_FAILED)
|
|
);
|
|
|
|
//
|
|
// Probably not a wheel mouse .. jump to the GetDeviceID2
|
|
// code
|
|
//
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2ACK;
|
|
|
|
}
|
|
break;
|
|
|
|
case ExpectingSetScaling1to1ACK:
|
|
case ExpectingSetScaling1to1ACK2:
|
|
case ExpectingSetScaling1to1ACK3:
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingIdAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
|
|
if (deviceExtension->InputResetSubState ==
|
|
ExpectingSetScaling1to1ACK3) {
|
|
|
|
//
|
|
// Read the status of the mouse (a 3 byte stream)
|
|
//
|
|
nextCommand = READ_MOUSE_STATUS;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingReadMouseStatusACK;
|
|
}
|
|
else {
|
|
deviceExtension->InputResetSubState++;
|
|
nextCommand = SET_MOUSE_SCALING_1TO1;
|
|
}
|
|
|
|
} else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = SET_MOUSE_SCALING_1TO1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_ERROR_DURING_BUTTONS_DETECT)
|
|
);
|
|
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2ACK;
|
|
|
|
}
|
|
break;
|
|
|
|
case ExpectingReadMouseStatusACK:
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingIdAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
|
|
//
|
|
// Get ready for the 3 bytes
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingReadMouseStatusByte1;
|
|
|
|
bSendCommand = FALSE;
|
|
|
|
} else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = READ_MOUSE_STATUS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_ERROR_DURING_BUTTONS_DETECT)
|
|
);
|
|
|
|
//
|
|
// Probably not a wheel mouse .. jump to the GetDeviceID2
|
|
// code
|
|
//
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2ACK;
|
|
|
|
}
|
|
break;
|
|
|
|
case ExpectingReadMouseStatusByte1:
|
|
IsrPrint(DBG_MOUISR_PNPID,
|
|
(pDumpExpecting,
|
|
(ULONG) 0x00,
|
|
(ULONG) byte
|
|
));
|
|
|
|
bSendCommand = FALSE;
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingReadMouseStatusByte2;
|
|
break;
|
|
|
|
case ExpectingReadMouseStatusByte2:
|
|
IsrPrint(DBG_MOUISR_PNPID,
|
|
(pDumpExpecting,
|
|
(ULONG) 0x00,
|
|
(ULONG) byte
|
|
));
|
|
|
|
bSendCommand = FALSE;
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingReadMouseStatusByte3;
|
|
|
|
//
|
|
// This will be the number of buttons
|
|
//
|
|
if (byte == 2 || byte == 3) {
|
|
deviceExtension->MouseAttributes.NumberOfButtons = byte;
|
|
}
|
|
else {
|
|
deviceExtension->MouseAttributes.NumberOfButtons =
|
|
MOUSE_NUMBER_OF_BUTTONS;
|
|
}
|
|
break;
|
|
|
|
case ExpectingReadMouseStatusByte3:
|
|
IsrPrint(DBG_MOUISR_PNPID,
|
|
(pDumpExpecting,
|
|
(ULONG) 0x00,
|
|
(ULONG) byte
|
|
));
|
|
|
|
bSendCommand = FALSE;
|
|
altCommand = POST_BUTTONDETECT_COMMAND;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
POST_BUTTONDETECT_COMMAND_SUBSTATE;
|
|
|
|
break;
|
|
|
|
case ExpectingSetResolutionDefaultACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Set the mouse refresh to the default
|
|
//
|
|
nextCommand = deviceExtension->Resolution;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetResolutionDefaultValueACK;
|
|
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = SET_MOUSE_RESOLUTION;
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_RESOLUTION_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
|
|
break;
|
|
|
|
case ExpectingSetResolutionDefaultValueACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Are we allowed to detect wether or not a wheel mouse
|
|
// is present?
|
|
//
|
|
if (deviceExtension->EnableWheelDetection == 2) {
|
|
//
|
|
// Begin the sequence to activate the mouse wheel
|
|
//
|
|
deviceExtension->InputResetSubState = EnableWheel;
|
|
goto SwitchOnInputResetSubState;
|
|
|
|
}
|
|
else if (deviceExtension->EnableWheelDetection == 1) {
|
|
//
|
|
// Begin the PNP id detection sequence
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
StartPnPIdDetection;
|
|
goto SwitchOnInputResetSubState;
|
|
}
|
|
else {
|
|
//
|
|
// Begin the sequence to set the default refresh
|
|
// rate
|
|
//
|
|
nextCommand = POST_WHEEL_DETECT_COMMAND;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
POST_WHEEL_DETECT_COMMAND_SUBSTATE;
|
|
}
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetResolutionDefaultACK;
|
|
|
|
resendCommand = SET_MOUSE_RESOLUTION;
|
|
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_RESOLUTION_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case ExpectingLoopSetSamplingRateACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Set the new sampling rate value
|
|
//
|
|
nextCommand = deviceExtension->SampleRates[
|
|
deviceExtension->SampleRatesIndex];
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingLoopSetSamplingRateValueACK;
|
|
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// The state stays the same, just resend the command
|
|
//
|
|
resendCommand = SET_MOUSE_SAMPLING_RATE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_SAMPLE_RATE_FAILED)
|
|
);
|
|
|
|
//
|
|
// Probably not a wheel mouse .. jump to the GetDeviceID2
|
|
// code
|
|
//
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2ACK;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ExpectingLoopSetSamplingRateValueACK:
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
("(%2d)\n",
|
|
deviceExtension->SampleRates[
|
|
deviceExtension->SampleRatesIndex]
|
|
));
|
|
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
if (deviceExtension->SampleRates[
|
|
++deviceExtension->SampleRatesIndex] ==
|
|
ONE_PAST_FINAL_SAMPLE) {
|
|
|
|
deviceExtension->InputResetSubState =
|
|
deviceExtension->PostSamplesState;
|
|
|
|
goto SwitchOnInputResetSubState;
|
|
}
|
|
else {
|
|
nextCommand = SET_MOUSE_SAMPLING_RATE;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingLoopSetSamplingRateACK;
|
|
}
|
|
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// The state stays the same, just resend the command
|
|
//
|
|
resendCommand = deviceExtension->SampleRates[
|
|
deviceExtension->SampleRatesIndex];
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_SAMPLE_RATE_FAILED)
|
|
);
|
|
|
|
//
|
|
// Probably not a wheel mouse .. jump to the GetDeviceID2
|
|
// code
|
|
//
|
|
bSendCommand = FALSE;
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2ACK;
|
|
|
|
}
|
|
break;
|
|
|
|
case ExpectingPnpId:
|
|
//
|
|
// Don't do anything but advance the state, the PnP ID will
|
|
// be "pushed" to the ISR
|
|
//
|
|
deviceExtension->InputResetSubState = ExpectingPnpIdByte1;
|
|
currentIdChar = deviceExtension->PnPID;
|
|
RtlZeroMemory(deviceExtension->PnPID,
|
|
MOUSE_PNPID_LENGTH * sizeof(WCHAR)
|
|
);
|
|
bSendCommand = FALSE;
|
|
|
|
break;
|
|
|
|
case ExpectingPnpIdByte2:
|
|
//
|
|
// Check to see if this an older MS mouse that gives back its ID
|
|
// in make AND BREAK codes (ugh!). If so, then just eat the
|
|
// remaining 6 (+6) bytes
|
|
//
|
|
if (deviceExtension->PnPID[0] == L'P' && byte == 0x99) {
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingLegacyPnpIdByte2_Make;
|
|
bSendCommand = FALSE;
|
|
break;
|
|
}
|
|
|
|
case ExpectingPnpIdByte1:
|
|
case ExpectingPnpIdByte3:
|
|
case ExpectingPnpIdByte4:
|
|
case ExpectingPnpIdByte5:
|
|
case ExpectingPnpIdByte6:
|
|
case ExpectingPnpIdByte7:
|
|
|
|
IsrPrint(DBG_MOUISR_PNPID,
|
|
("ExpectingPnpIdByte%1d (0x%2x)\n",
|
|
(ULONG) deviceExtension->InputResetSubState -
|
|
ExpectingPnpIdByte1 + 1,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte < ScanCodeToUCharCount) {
|
|
*currentIdChar = (WCHAR) ScanCodeToUChar[byte];
|
|
}
|
|
else {
|
|
*currentIdChar = L'?';
|
|
}
|
|
currentIdChar++;
|
|
|
|
bSendCommand = FALSE;
|
|
if (deviceExtension->InputResetSubState ==
|
|
ExpectingPnpIdByte7) {
|
|
if (I8xVerifyMousePnPID(deviceExtension,
|
|
deviceExtension->PnPID)) {
|
|
//
|
|
// We are know know for sure that we have a wheel
|
|
// mouse on this system. However, we will update
|
|
// our date structures after the enable has gone
|
|
// through since that simplifies things a great deal
|
|
//
|
|
deviceExtension->InputResetSubState = EnableWheel;
|
|
goto SwitchOnInputResetSubState;
|
|
}
|
|
else {
|
|
//
|
|
// Oops, not our device, so lets stop the sequence
|
|
// now by sending it a GET_DEVICE_ID
|
|
//
|
|
altCommand = GET_DEVICE_ID;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2ACK;
|
|
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(deviceExtension->InputResetSubState >=
|
|
ExpectingPnpIdByte1);
|
|
ASSERT(deviceExtension->InputResetSubState <
|
|
ExpectingPnpIdByte7);
|
|
|
|
deviceExtension->InputResetSubState++;
|
|
}
|
|
break;
|
|
|
|
case ExpectingLegacyPnpIdByte2_Make:
|
|
case ExpectingLegacyPnpIdByte2_Break:
|
|
case ExpectingLegacyPnpIdByte3_Make:
|
|
case ExpectingLegacyPnpIdByte3_Break:
|
|
case ExpectingLegacyPnpIdByte4_Make:
|
|
case ExpectingLegacyPnpIdByte4_Break:
|
|
case ExpectingLegacyPnpIdByte5_Make:
|
|
case ExpectingLegacyPnpIdByte5_Break:
|
|
case ExpectingLegacyPnpIdByte6_Make:
|
|
case ExpectingLegacyPnpIdByte6_Break:
|
|
case ExpectingLegacyPnpIdByte7_Make:
|
|
//
|
|
// Just eat the byte
|
|
//
|
|
deviceExtension->InputResetSubState++;
|
|
bSendCommand = FALSE;
|
|
break;
|
|
|
|
case ExpectingLegacyPnpIdByte7_Break:
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
bSendCommand = FALSE;
|
|
|
|
altCommand = GET_DEVICE_ID;
|
|
deviceExtension->InputResetSubState = ExpectingGetDeviceId2ACK;
|
|
break;
|
|
|
|
case PostWheelDetectState:
|
|
bSendCommand = FALSE;
|
|
altCommand = POST_WHEEL_DETECT_COMMAND;
|
|
|
|
//
|
|
// Best possible next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
POST_WHEEL_DETECT_COMMAND_SUBSTATE;
|
|
break;
|
|
|
|
case ExpectingGetDeviceId2ACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingGetDeviceId2Value;
|
|
|
|
bSendCommand = FALSE;
|
|
|
|
} else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = GET_DEVICE_ID;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_GET_DEVICE_ID_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case ExpectingGetDeviceId2Value:
|
|
IsrPrint(DBG_MOUISR_PNPID,
|
|
("got a device ID of %2d\n",
|
|
(ULONG) byte
|
|
));
|
|
|
|
CLEAR_HW_FLAGS(WHEELMOUSE_HARDWARE_PRESENT | FIVE_BUTTON_HARDWARE_PRESENT);
|
|
SET_HW_FLAGS(MOUSE_HARDWARE_PRESENT);
|
|
switch (byte) {
|
|
case MOUSE_ID_BYTE:
|
|
//
|
|
// Mouse Present, but no wheel
|
|
//
|
|
deviceExtension->MouseAttributes.MouseIdentifier =
|
|
MOUSE_I8042_HARDWARE;
|
|
|
|
if (deviceExtension->NumberOfButtonsOverride != 0) {
|
|
deviceExtension->MouseAttributes.NumberOfButtons =
|
|
deviceExtension->NumberOfButtonsOverride;
|
|
}
|
|
else {
|
|
//
|
|
// Number of buttons is determined in the
|
|
// ExpectingReadMouseStatusByte2 case
|
|
//
|
|
// Number of buttons determined in
|
|
;
|
|
}
|
|
|
|
break;
|
|
|
|
case WHEELMOUSE_ID_BYTE:
|
|
//
|
|
// Update the HardwarePresent to show a Z mouse is
|
|
// operational and set the appropraite mouse type flags
|
|
//
|
|
SET_HW_FLAGS(WHEELMOUSE_HARDWARE_PRESENT);
|
|
|
|
deviceExtension->MouseAttributes.MouseIdentifier =
|
|
WHEELMOUSE_I8042_HARDWARE;
|
|
|
|
deviceExtension->MouseAttributes.NumberOfButtons = 3;
|
|
break;
|
|
|
|
case FIVEBUTTON_ID_BYTE:
|
|
//
|
|
// Update the HardwarePresent to show a 5 button wheel mouse
|
|
// is operational and set the appropraite mouse type flags
|
|
//
|
|
SET_HW_FLAGS(FIVE_BUTTON_HARDWARE_PRESENT | WHEELMOUSE_HARDWARE_PRESENT);
|
|
deviceExtension->MouseAttributes.MouseIdentifier =
|
|
WHEELMOUSE_I8042_HARDWARE;
|
|
|
|
deviceExtension->MouseAttributes.NumberOfButtons = 5;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Make sure to log the problem
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_MOU_RESET_RESPONSE_FAILED)
|
|
);
|
|
|
|
Print(DBG_MOUISR_RESETTING, ("clearing mouse (no response)\n"));
|
|
CLEAR_MOUSE_PRESENT();
|
|
|
|
deviceExtension->MouseAttributes.NumberOfButtons = 0;
|
|
deviceExtension->MouseAttributes.MouseIdentifier = 0;
|
|
|
|
//
|
|
// Set up the state machine as best we can
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
|
|
|
|
//
|
|
// Send down the command to set a new sampling rate
|
|
//
|
|
bSendCommand = FALSE;
|
|
altCommand = SET_MOUSE_SAMPLING_RATE;
|
|
|
|
//
|
|
// This is our next state
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetSamplingRateACK;
|
|
|
|
break;
|
|
|
|
case ExpectingSetSamplingRateACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Set the mouse refresh rate to its final value
|
|
//
|
|
nextCommand =
|
|
(UCHAR) deviceExtension->MouseAttributes.SampleRate;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetSamplingRateValueACK;
|
|
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = SET_MOUSE_SAMPLING_RATE;
|
|
}
|
|
else {
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_SAMPLE_RATE_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case ExpectingSetSamplingRateValueACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Let's set the resolution once more to be sure.
|
|
//
|
|
nextCommand = SET_MOUSE_RESOLUTION;
|
|
|
|
//
|
|
// We go back to expecting an ACK
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingFinalResolutionACK;
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = SET_MOUSE_SAMPLING_RATE;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingSetSamplingRateACK;
|
|
}
|
|
else {
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_SAMPLE_RATE_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case ExpectingFinalResolutionACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Set the mouse refresh rate to its final value
|
|
//
|
|
nextCommand =
|
|
(UCHAR) deviceExtension->Resolution;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingFinalResolutionValueACK;
|
|
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = SET_MOUSE_RESOLUTION;
|
|
}
|
|
else {
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_RESOLUTION_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case ExpectingFinalResolutionValueACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Finally! Enable the mouse and we are done
|
|
//
|
|
nextCommand = ENABLE_MOUSE_TRANSMISSION;
|
|
|
|
//
|
|
// We go back to expecting an ACK
|
|
//
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingEnableACK;
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = SET_MOUSE_RESOLUTION;
|
|
|
|
deviceExtension->InputResetSubState =
|
|
ExpectingFinalResolutionACK;
|
|
}
|
|
else {
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_SET_RESOLUTION_FAILED)
|
|
);
|
|
|
|
//
|
|
// We didn't get an ACK on this? Boggle. Okay, let's
|
|
// reset the mouse (probably AGAIN) and try to figure
|
|
// things out one more time
|
|
//
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case ExpectingEnableACK:
|
|
|
|
IsrPrint(DBG_MOUISR_ACK,
|
|
(pDumpExpectingAck,
|
|
(ULONG) ACKNOWLEDGE,
|
|
(ULONG) byte
|
|
));
|
|
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
//
|
|
// Done and reset the number of possible mouse resets
|
|
//
|
|
deviceExtension->InputState = MouseIdle;
|
|
I8X_MOUSE_INIT_COUNTERS(deviceExtension);
|
|
|
|
deviceExtension->CurrentInput.Flags |=
|
|
MOUSE_ATTRIBUTES_CHANGED;
|
|
I8xQueueCurrentMouseInput(DeviceObject);
|
|
|
|
ASSERT(DeviceObject->CurrentIrp ==
|
|
deviceExtension->ResetIrp);
|
|
|
|
ASSERT(deviceExtension->ResetIrp != NULL);
|
|
ASSERT(DeviceObject->CurrentIrp != NULL);
|
|
|
|
//
|
|
// CurrentIrp is == deviceExtension->ResetIrp
|
|
//
|
|
IoRequestDpc(DeviceObject,
|
|
// DeviceObject->CurrentIrp,
|
|
deviceExtension->ResetIrp,
|
|
IntToPtr(IsrDpcCauseMouseResetComplete)
|
|
);
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
|
|
//
|
|
// Resend the "Enable Mouse Transmission" sequence.
|
|
//
|
|
// NOTE: This is a workaround for the Olivetti MIPS machine,
|
|
// which sends a resend response if a key is held down
|
|
// while we're attempting the I8xMouseEnableTransmission.
|
|
//
|
|
resendCommand = ENABLE_MOUSE_TRANSMISSION;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// We could not understand if we were able to reenable the
|
|
// mouse... Best bet here is to also reset the mouse.
|
|
//
|
|
// Log the error
|
|
//
|
|
KeInsertQueueDpc(&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_ENABLE_FAILED)
|
|
);
|
|
|
|
goto IsrResetMouse;
|
|
}
|
|
break;
|
|
|
|
case MouseResetFailed:
|
|
//
|
|
// We have failed to reset the mouse, just ignore all further
|
|
// data. The ResetSubState will be reset if / when the user
|
|
// tries to reset the mouse via a plug in
|
|
//
|
|
return TRUE;
|
|
|
|
default:
|
|
|
|
//
|
|
// This is our bad state
|
|
//
|
|
IsrPrint(DBG_MOUISR_ERROR | DBG_MOUISR_STATE,
|
|
(" INVALID RESET SUBSTATE %d\n",
|
|
deviceExtension->InputResetSubState
|
|
));
|
|
|
|
//
|
|
// Queue a DPC to log an internal driver error.
|
|
//
|
|
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_INVALID_ISR_STATE_MOU)
|
|
);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
} // switch (deviceExtension->MouseExtension.InputResetSubState)
|
|
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
|
|
//
|
|
// This is our bad state
|
|
//
|
|
IsrPrint(DBG_MOUISR_ERROR | DBG_MOUISR_STATE,
|
|
(" INVALID STATE %d\n",
|
|
deviceExtension->InputState
|
|
));
|
|
|
|
//
|
|
// Queue a DPC to log an internal driver error.
|
|
//
|
|
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_INVALID_ISR_STATE_MOU)
|
|
);
|
|
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (deviceExtension->InputState == MouseResetting) {
|
|
if (bSendCommand) {
|
|
if (byte == (UCHAR) ACKNOWLEDGE) {
|
|
I8X_WRITE_CMD_TO_MOUSE();
|
|
I8X_MOUSE_COMMAND( nextCommand );
|
|
RECORD_ISR_STATE_COMMAND(deviceExtension, nextCommand);
|
|
}
|
|
else if (byte == (UCHAR) RESEND) {
|
|
if (deviceExtension->ResendCount++ < MOUSE_RESENDS_MAX) {
|
|
I8X_WRITE_CMD_TO_MOUSE();
|
|
I8X_MOUSE_COMMAND( resendCommand );
|
|
RECORD_ISR_STATE_COMMAND(deviceExtension, resendCommand);
|
|
}
|
|
else {
|
|
//
|
|
// Got too many resends, try a (possible) reset
|
|
//
|
|
deviceExtension->ResendCount = 0;
|
|
goto IsrResetMouse;
|
|
}
|
|
}
|
|
}
|
|
else if (altCommand) {
|
|
I8X_WRITE_CMD_TO_MOUSE();
|
|
I8X_MOUSE_COMMAND( altCommand );
|
|
RECORD_ISR_STATE_COMMAND(deviceExtension, altCommand);
|
|
}
|
|
|
|
if (byte != (UCHAR) RESEND) {
|
|
deviceExtension->ResendCount = 0;
|
|
}
|
|
}
|
|
|
|
|
|
IsrPrint(DBG_MOUISR_TRACE, ("exit\n"));
|
|
|
|
return TRUE;
|
|
|
|
IsrResetMouse:
|
|
//
|
|
// About 1/2 of the errors in the resetting state machine are caused by
|
|
// trying to see if the wheel on the mouse exists...just try to enable it
|
|
// from now on....
|
|
//
|
|
if (deviceExtension->EnableWheelDetection == 1) {
|
|
deviceExtension->EnableWheelDetection = 2;
|
|
}
|
|
|
|
IsrResetMouseOnly:
|
|
deviceExtension->InputResetSubState = QueueingMouseReset;
|
|
KeInsertQueueDpc(&deviceExtension->MouseIsrResetDpc,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
return ret;
|
|
#undef TRANSITION_UP
|
|
#undef TRANSITION_DOWN
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xInitializeMouse(
|
|
IN PPORT_MOUSE_EXTENSION MouseExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the i8042 mouse hardware. It is called
|
|
only at initialization, and does not synchronize access to the hardware.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Return Value:
|
|
|
|
Returns status.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define DUMP_COUNT 4
|
|
|
|
NTSTATUS errorCode = STATUS_SUCCESS;
|
|
NTSTATUS status;
|
|
PPORT_MOUSE_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
UCHAR byte;
|
|
UCHAR numButtons;
|
|
ULONG dumpData[DUMP_COUNT];
|
|
ULONG dumpCount = 0;
|
|
ULONG i;
|
|
ULONG uniqueErrorValue;
|
|
LARGE_INTEGER li,
|
|
startOfSpin,
|
|
nextQuery,
|
|
difference,
|
|
tenSeconds;
|
|
BOOLEAN okToLogError;
|
|
|
|
PAGED_CODE();
|
|
|
|
Print(DBG_SS_TRACE, ("I8xInitializeMouse enter\n"));
|
|
|
|
//
|
|
// Initialize this array
|
|
//
|
|
for (i = 0; i < DUMP_COUNT; i++) {
|
|
dumpData[i] = 0;
|
|
}
|
|
|
|
//
|
|
// Get the device extension.
|
|
//
|
|
deviceExtension = MouseExtension;
|
|
deviceObject = deviceExtension->Self;
|
|
okToLogError = TRUE;
|
|
|
|
//
|
|
// Reset the mouse. Send a Write To Auxiliary Device command to the
|
|
// 8042 controller. Then send the Reset Mouse command to the mouse
|
|
// through the 8042 data register. Expect to get back an ACK, followed
|
|
// by a completion code and the ID code (0x00).
|
|
//
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) MOUSE_RESET
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_ERROR,
|
|
("%s failed mouse reset, status 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Only log this error if the user wants to see it
|
|
//
|
|
okToLogError = Globals.ReportResetErrors;
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
// Use NO_MOU_DEVICE instead of I8042_MOU_RESET_COMMAND_FAILED because
|
|
// it is a clearer message.
|
|
//
|
|
errorCode = I8042_NO_MOU_DEVICE;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 415;
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
dumpData[3] = MOUSE_RESET;
|
|
dumpCount = 4;
|
|
|
|
status = STATUS_DEVICE_NOT_CONNECTED;
|
|
SET_HW_FLAGS(PHANTOM_MOUSE_HARDWARE_REPORTED);
|
|
|
|
goto I8xInitializeMouseExit;
|
|
}
|
|
|
|
deviceExtension->ResendCount = 0;
|
|
I8X_MOUSE_INIT_COUNTERS(deviceExtension);
|
|
|
|
//
|
|
// Get the mouse reset responses. The first response should be a
|
|
// MOUSE_COMPLETE. The second response should be the mouse ID.
|
|
// Note that it is usually necessary to stall a long time to get the
|
|
// mouse reset/self-test to work.
|
|
//
|
|
li.QuadPart = -100;
|
|
|
|
tenSeconds.QuadPart = 10*10*1000*1000;
|
|
KeQueryTickCount(&startOfSpin);
|
|
|
|
while (1) {
|
|
status = I8xGetBytePolled(
|
|
(CCHAR) ControllerDeviceType,
|
|
&byte
|
|
);
|
|
|
|
if (NT_SUCCESS(status) && (byte == (UCHAR) MOUSE_COMPLETE)) {
|
|
//
|
|
// The reset completed successfully.
|
|
//
|
|
break;
|
|
}
|
|
else {
|
|
//
|
|
// Stall, and then try again to get a response from
|
|
// the reset.
|
|
//
|
|
if (status == STATUS_IO_TIMEOUT) {
|
|
//
|
|
// Stall, and then try again to get a response from
|
|
// the reset.
|
|
//
|
|
KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&li);
|
|
|
|
KeQueryTickCount(&nextQuery);
|
|
|
|
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
|
|
|
|
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
|
|
if (difference.QuadPart*KeQueryTimeIncrement() >=
|
|
tenSeconds.QuadPart) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_ERROR,
|
|
("%s failed reset response 1, status 0x%x, byte 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status,
|
|
byte
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_MOU_RESET_RESPONSE_FAILED;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 420;
|
|
dumpData[0] = KBDMOU_INCORRECT_RESPONSE;
|
|
dumpData[1] = ControllerDeviceType;
|
|
dumpData[2] = MOUSE_COMPLETE;
|
|
dumpData[3] = byte;
|
|
dumpCount = 4;
|
|
|
|
goto I8xInitializeMouseExit;
|
|
}
|
|
|
|
status = I8xGetBytePolled(
|
|
(CCHAR) ControllerDeviceType,
|
|
&byte
|
|
);
|
|
|
|
if ((!NT_SUCCESS(status)) || (byte != MOUSE_ID_BYTE)) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s failed reset response 2, status 0x%x, byte 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status,
|
|
byte
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_MOU_RESET_RESPONSE_FAILED;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 425;
|
|
dumpData[0] = KBDMOU_INCORRECT_RESPONSE;
|
|
dumpData[1] = ControllerDeviceType;
|
|
dumpData[2] = MOUSE_ID_BYTE;
|
|
dumpData[3] = byte;
|
|
dumpCount = 4;
|
|
|
|
goto I8xInitializeMouseExit;
|
|
}
|
|
|
|
//
|
|
// If we are going to initialize the mouse via the interrupt (the default),
|
|
// then quit here
|
|
//
|
|
if (!deviceExtension->InitializePolled) {
|
|
Print(DBG_SS_NOISE, ("Initializing via the interrupt\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Print(DBG_SS_NOISE, ("Initializing polled\n"));
|
|
|
|
deviceExtension->EnableMouse.FirstTime = TRUE;
|
|
deviceExtension->EnableMouse.Enabled = TRUE;
|
|
deviceExtension->EnableMouse.Count = 0;
|
|
|
|
//
|
|
// Check to see if this is a wheel mouse
|
|
//
|
|
I8xFindWheelMouse(deviceExtension);
|
|
|
|
//
|
|
// Try to detect the number of mouse buttons.
|
|
//
|
|
status = I8xQueryNumberOfMouseButtons(&numButtons);
|
|
|
|
Print(DBG_SS_INFO,
|
|
("num buttons returned (%d), num butons in attrib (%d)\n"
|
|
"\t(if 0, then no logitech detection support)\n",
|
|
numButtons,
|
|
deviceExtension->MouseAttributes.NumberOfButtons
|
|
));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_ERROR,
|
|
("%s: failed to get buttons, status 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_ERROR_DURING_BUTTONS_DETECT;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 426;
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
dumpCount = 3;
|
|
|
|
goto I8xInitializeMouseExit;
|
|
|
|
} else if (numButtons) {
|
|
|
|
deviceExtension->MouseAttributes.NumberOfButtons =
|
|
numButtons;
|
|
|
|
}
|
|
|
|
//
|
|
// If there is a 5 button mouse, report it.
|
|
// If there is a wheel, hardcode the number of buttons to three
|
|
//
|
|
if (FIVE_PRESENT()) {
|
|
deviceExtension->MouseAttributes.NumberOfButtons = 5;
|
|
}
|
|
else if (WHEEL_PRESENT()) {
|
|
deviceExtension->MouseAttributes.NumberOfButtons = 3;
|
|
}
|
|
|
|
|
|
//
|
|
// Set mouse sampling rate. Send a Write To Auxiliary Device command
|
|
// to the 8042 controller. Then send the Set Mouse Sampling Rate
|
|
// command to the mouse through the 8042 data register,
|
|
// followed by its parameter.
|
|
//
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) SET_MOUSE_SAMPLING_RATE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: failed write set sample rate, status 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_SET_SAMPLE_RATE_FAILED;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 435;
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
dumpData[3] = SET_MOUSE_SAMPLING_RATE;
|
|
dumpCount = 4;
|
|
|
|
goto I8xInitializeMouseExit;
|
|
|
|
}
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) deviceExtension->MouseAttributes.SampleRate
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: failed write sample rate, status 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_SET_SAMPLE_RATE_FAILED;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 445;
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
dumpData[3] = deviceExtension->MouseAttributes.SampleRate;
|
|
dumpCount = 4;
|
|
|
|
goto I8xInitializeMouseExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the mouse resolution. Send a Write To Auxiliary Device command
|
|
// to the 8042 controller. Then send the Set Mouse Resolution
|
|
// command to the mouse through the 8042 data register,
|
|
// followed by its parameter.
|
|
//
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) SET_MOUSE_RESOLUTION
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: failed write set resolution, status 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_SET_RESOLUTION_FAILED;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 455;
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
dumpData[3] = SET_MOUSE_RESOLUTION;
|
|
dumpCount = 4;
|
|
|
|
goto I8xInitializeMouseExit;
|
|
|
|
}
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) deviceExtension->Resolution
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: failed set mouse resolution, status 0x%x\n",
|
|
pFncInitializeMouse,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_SET_RESOLUTION_FAILED;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 465;
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
dumpData[3] = deviceExtension->Resolution;
|
|
dumpCount = 4;
|
|
|
|
goto I8xInitializeMouseExit;
|
|
|
|
}
|
|
|
|
I8xInitializeMouseExit:
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// The mouse initialization failed. Log an error.
|
|
//
|
|
if (errorCode != STATUS_SUCCESS && okToLogError) {
|
|
I8xLogError(deviceObject,
|
|
errorCode,
|
|
uniqueErrorValue,
|
|
status,
|
|
dumpData,
|
|
dumpCount
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize current mouse input packet state.
|
|
//
|
|
deviceExtension->PreviousSignAndOverflow = 0;
|
|
deviceExtension->InputState = MouseExpectingACK;
|
|
deviceExtension->InputResetSubState = 0;
|
|
deviceExtension->LastByteReceived = 0;
|
|
|
|
Print(DBG_SS_TRACE,
|
|
("%s, %s\n",
|
|
pFncInitializeMouse,
|
|
pExit
|
|
));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xMouseConfiguration(
|
|
IN PPORT_MOUSE_EXTENSION MouseExtension,
|
|
IN PCM_RESOURCE_LIST ResourceList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the configuration information for the mouse.
|
|
|
|
Arguments:
|
|
|
|
MouseExtension - Mouse extension
|
|
|
|
ResourceList - Translated resource list give to us via the start IRP
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if all the resources required are presented
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PCM_PARTIAL_RESOURCE_LIST partialResList = NULL;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR firstResDesc = NULL,
|
|
currentResDesc = NULL;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullResDesc = NULL;
|
|
PI8042_CONFIGURATION_INFORMATION configuration;
|
|
|
|
ULONG count,
|
|
i;
|
|
|
|
KINTERRUPT_MODE defaultInterruptMode;
|
|
BOOLEAN defaultInterruptShare;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!ResourceList) {
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR, ("mouse with no resources\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
fullResDesc = ResourceList->List;
|
|
if (!fullResDesc) {
|
|
//
|
|
// this should never happen
|
|
//
|
|
ASSERT(fullResDesc != NULL);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
SET_HW_FLAGS(MOUSE_HARDWARE_PRESENT);
|
|
configuration = &Globals.ControllerData->Configuration;
|
|
|
|
partialResList = &fullResDesc->PartialResourceList;
|
|
currentResDesc = firstResDesc = partialResList->PartialDescriptors;
|
|
count = partialResList->Count;
|
|
|
|
configuration->FloatingSave = I8042_FLOATING_SAVE;
|
|
configuration->BusNumber = fullResDesc->BusNumber;
|
|
configuration->InterfaceType = fullResDesc->InterfaceType;
|
|
|
|
if (configuration->InterfaceType == MicroChannel) {
|
|
defaultInterruptShare = TRUE;
|
|
defaultInterruptMode = LevelSensitive;
|
|
}
|
|
else {
|
|
defaultInterruptShare = I8042_INTERRUPT_SHARE;
|
|
defaultInterruptMode = I8042_INTERRUPT_MODE;
|
|
}
|
|
|
|
//
|
|
// NOTE: not all of the resources associated with the i8042 may be given at
|
|
// this time. From empirical tests, the mouse is only associated with its
|
|
// interrupt, while the keyboard will receive the ports along with its
|
|
// interrupt
|
|
//
|
|
for (i = 0; i < count; i++, currentResDesc++) {
|
|
switch (currentResDesc->Type) {
|
|
case CmResourceTypeMemory:
|
|
Globals.RegistersMapped = TRUE;
|
|
|
|
case CmResourceTypePort:
|
|
//
|
|
// Copy the port information. We will sort the port list
|
|
// into ascending order based on the starting port address
|
|
// later (note that we *know* there are a max of two port
|
|
// ranges for the i8042).
|
|
//
|
|
#if 0
|
|
if (currentResDesc->Flags == CM_RESOURCE_PORT_MEMORY) {
|
|
Globals.RegistersMapped = TRUE;
|
|
}
|
|
#endif
|
|
|
|
Print(DBG_SS_NOISE, ("io flags are 0x%x\n", currentResDesc->Flags));
|
|
|
|
if (configuration->PortListCount < MaximumPortCount) {
|
|
configuration->PortList[configuration->PortListCount] =
|
|
*currentResDesc;
|
|
configuration->PortList[configuration->PortListCount].ShareDisposition =
|
|
I8042_REGISTER_SHARE ? CmResourceShareShared:
|
|
CmResourceShareDriverExclusive;
|
|
configuration->PortListCount += 1;
|
|
}
|
|
else {
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("Mouse::PortListCount already at max (%d)",
|
|
configuration->PortListCount
|
|
));
|
|
}
|
|
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
//
|
|
// Copy the interrupt information.
|
|
//
|
|
MouseExtension->InterruptDescriptor = *currentResDesc;
|
|
MouseExtension->InterruptDescriptor.ShareDisposition =
|
|
defaultInterruptShare ? CmResourceShareShared :
|
|
CmResourceShareDeviceExclusive;
|
|
|
|
break;
|
|
|
|
default:
|
|
Print(DBG_ALWAYS,
|
|
("resource type 0x%x unhandled...\n",
|
|
(LONG) currentResDesc->Type
|
|
));
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
MouseExtension->MouseAttributes.MouseIdentifier = MOUSE_I8042_HARDWARE;
|
|
|
|
//
|
|
// If no interrupt configuration information was found, use the
|
|
// mouse driver defaults.
|
|
//
|
|
if (!(MouseExtension->InterruptDescriptor.Type & CmResourceTypeInterrupt)) {
|
|
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("Using default mouse interrupt config\n"
|
|
));
|
|
|
|
MouseExtension->InterruptDescriptor.Type = CmResourceTypeInterrupt;
|
|
MouseExtension->InterruptDescriptor.ShareDisposition =
|
|
defaultInterruptShare ? CmResourceShareShared :
|
|
CmResourceShareDeviceExclusive;
|
|
MouseExtension->InterruptDescriptor.Flags =
|
|
(defaultInterruptMode == Latched) ? CM_RESOURCE_INTERRUPT_LATCHED :
|
|
CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
MouseExtension->InterruptDescriptor.u.Interrupt.Level = MOUSE_IRQL;
|
|
MouseExtension->InterruptDescriptor.u.Interrupt.Vector = MOUSE_VECTOR;
|
|
|
|
// MouseExtension->ReportInterrupt = TRUE;
|
|
}
|
|
|
|
Print(DBG_SS_INFO,
|
|
("Mouse interrupt config --\n"
|
|
"%s, %s, Irq = 0x%x\n",
|
|
MouseExtension->InterruptDescriptor.ShareDisposition == CmResourceShareShared?
|
|
"Sharable" : "NonSharable",
|
|
MouseExtension->InterruptDescriptor.Flags == CM_RESOURCE_INTERRUPT_LATCHED?
|
|
"Latched" : "Level Sensitive",
|
|
MouseExtension->InterruptDescriptor.u.Interrupt.Vector
|
|
));
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
SET_HW_FLAGS(MOUSE_HARDWARE_INITIALIZED);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xQueryNumberOfMouseButtons(
|
|
OUT PUCHAR NumberOfMouseButtons
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This implements logitech's method for detecting the number of
|
|
mouse buttons. If anything doesn't go as expected then 0
|
|
is returned.
|
|
|
|
Calling this routine will set the mouse resolution to something
|
|
really low. The mouse resolution should be reset after this
|
|
call.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
NumberOfMouseButtons - Returns the number of mouse buttons or 0 if
|
|
the device did not support this type of
|
|
mouse button detection.
|
|
|
|
Return Value:
|
|
|
|
An NTSTATUS code indicating success or failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UCHAR byte;
|
|
UCHAR buttons;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// First we need to send down a set resolution command
|
|
//
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) SET_MOUSE_RESOLUTION
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// This is another part of the data packet to get the info we want
|
|
//
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) 0x00
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) SET_MOUSE_SCALING_1TO1
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) READ_MOUSE_STATUS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
status = I8xGetBytePolled((CCHAR) ControllerDeviceType, &byte);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
Print(DBG_SS_NOISE, ("Query Buttons, 1st byte: 0x%2x\n", byte));
|
|
|
|
status = I8xGetBytePolled((CCHAR) ControllerDeviceType, &buttons);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
Print(DBG_SS_NOISE, ("Query Buttons, 2nd byte: 0x%2x\n", buttons));
|
|
|
|
status = I8xGetBytePolled((CCHAR) ControllerDeviceType, &byte);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
Print(DBG_SS_NOISE, ("Query Buttons, 3rd byte: 0x%2x\n", byte));
|
|
|
|
if (buttons == 2 || buttons == 3) {
|
|
*NumberOfMouseButtons = buttons;
|
|
Print(DBG_SS_NOISE, ("Query Buttons found %2x", *NumberOfMouseButtons));
|
|
}
|
|
else {
|
|
*NumberOfMouseButtons = 0;
|
|
Print(DBG_SS_NOISE, ("Query Buttons -- not supported\n"));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xMouseEnableTransmission(
|
|
IN PPORT_MOUSE_EXTENSION MouseExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends an Enable command to the mouse hardware, causing
|
|
the mouse to begin transmissions. It is called at initialization
|
|
time, but only after the interrupt has been connected. This is
|
|
necessary so the driver can keep its notion of the mouse input data
|
|
state in sync with the hardware (i.e., for this type of mouse there is no
|
|
way to differentiate the first byte of a packet; if the user is randomly
|
|
moving the mouse during boot/initialization, the first mouse interrupt we
|
|
receive following IoConnectInterrupt could be for a byte that is not the
|
|
start of a packet, and we have no way to know that).
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Return Value:
|
|
|
|
Returns status.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define DUMP_COUNT 4
|
|
NTSTATUS errorCode = STATUS_SUCCESS;
|
|
NTSTATUS status;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
ULONG dumpCount = 0;
|
|
ULONG dumpData[DUMP_COUNT];
|
|
ULONG i;
|
|
ULONG uniqueErrorValue;
|
|
LARGE_INTEGER li;
|
|
PPORT_MOUSE_EXTENSION mouseExtension;
|
|
|
|
Print(DBG_SS_TRACE,
|
|
("%s: %s\n",
|
|
pFncMouseEnable,
|
|
pEnter
|
|
));
|
|
|
|
//
|
|
// Initialize the dump structure
|
|
//
|
|
for (i = 0; i < DUMP_COUNT; i++) {
|
|
|
|
dumpData[i] = 0;
|
|
|
|
}
|
|
|
|
if (MouseExtension->EnableMouse.FirstTime) {
|
|
// 5 seconds
|
|
li.QuadPart = -5 * 10 // from 100 ns to us
|
|
* 1000 // us to ms
|
|
* 1000; // ms to s
|
|
MouseExtension->EnableMouse.FirstTime = FALSE;
|
|
|
|
KeSetTimerEx(
|
|
&MouseExtension->EnableMouse.Timer,
|
|
li,
|
|
5 * 1000, // ms to s
|
|
&MouseExtension->EnableMouse.Dpc
|
|
);
|
|
}
|
|
|
|
//
|
|
// Re-enable the mouse at the mouse hardware, so that it can transmit
|
|
// data packets in continuous mode. Note that this is not the same
|
|
// as enabling the mouse device at the 8042 controller. The mouse
|
|
// hardware is sent an Enable command here, because it was
|
|
// Disabled as a result of the mouse reset command performed
|
|
// in I8xInitializeMouse().
|
|
//
|
|
// Note that we don't wait for an ACKNOWLEDGE back. The
|
|
// ACKNOWLEDGE back will actually cause a mouse interrupt, which
|
|
// then gets handled in the mouse ISR.
|
|
//
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
NO_WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) ENABLE_MOUSE_TRANSMISSION
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: "
|
|
"failed write enable transmission, status 0x%x\n",
|
|
pFncMouseEnable,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Set up error log info.
|
|
//
|
|
errorCode = I8042_MOU_ENABLE_XMIT;
|
|
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 475;
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
dumpData[3] = ENABLE_MOUSE_TRANSMISSION;
|
|
dumpCount = 4;
|
|
|
|
goto I8xEnableMouseTransmissionExit;
|
|
}
|
|
|
|
I8xEnableMouseTransmissionExit:
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// The mouse initialization failed. Log an error.
|
|
//
|
|
if (errorCode != STATUS_SUCCESS) {
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(
|
|
MouseExtension->Self, (UCHAR)
|
|
(sizeof(IO_ERROR_LOG_PACKET) + (dumpCount * sizeof(ULONG)))
|
|
);
|
|
|
|
if (errorLogEntry != NULL) {
|
|
|
|
errorLogEntry->ErrorCode = errorCode;
|
|
errorLogEntry->DumpDataSize = (USHORT) dumpCount * sizeof(ULONG);
|
|
errorLogEntry->SequenceNumber = 0;
|
|
errorLogEntry->MajorFunctionCode = 0;
|
|
errorLogEntry->IoControlCode = 0;
|
|
errorLogEntry->RetryCount = 0;
|
|
errorLogEntry->UniqueErrorValue = uniqueErrorValue;
|
|
errorLogEntry->FinalStatus = status;
|
|
for (i = 0; i < dumpCount; i++) {
|
|
|
|
errorLogEntry->DumpData[i] = dumpData[i];
|
|
|
|
}
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize current mouse input packet state
|
|
//
|
|
MouseExtension->PreviousSignAndOverflow = 0;
|
|
MouseExtension->InputState = MouseExpectingACK;
|
|
|
|
Print(DBG_SS_TRACE, ("I8xMouseEnableTransmission (0x%x)\n", status));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xTransmitByteSequence(
|
|
PUCHAR Bytes,
|
|
ULONG* UniqueErrorValue,
|
|
ULONG* ErrorCode,
|
|
ULONG* DumpData,
|
|
ULONG* DumpCount
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG byteCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = STATUS_SUCCESS;
|
|
byteCount = 0;
|
|
|
|
//
|
|
// Begin sending commands to the mouse
|
|
//
|
|
while (Bytes[byteCount] != 0) {
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
Bytes[byteCount]
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_ERROR,
|
|
("%s, failed write set sample rate #%d, status 0x%x\n",
|
|
pFncFindWheelMouse,
|
|
byteCount,
|
|
status
|
|
));
|
|
|
|
//
|
|
// Set up error log info
|
|
//
|
|
*ErrorCode = I8042_SET_SAMPLE_RATE_FAILED;
|
|
*DumpCount = 4;
|
|
DumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
DumpData[1] = DataPort;
|
|
DumpData[2] = I8042_WRITE_TO_AUXILIARY_DEVICE;
|
|
DumpData[3] = Bytes[byteCount];
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Next command
|
|
//
|
|
byteCount++;
|
|
(*UniqueErrorValue) += 5;
|
|
KeStallExecutionProcessor(50);
|
|
} // while
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xGetBytePolledIterated(
|
|
IN CCHAR DeviceType,
|
|
OUT PUCHAR Byte,
|
|
ULONG Attempts
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Try to get a single character
|
|
//
|
|
for(i = 0; i < Attempts; i++) {
|
|
status = I8xGetBytePolled(
|
|
(CCHAR) ControllerDeviceType,
|
|
Byte
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Read was successfull. We got a byte.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the read timed out, stall and retry.
|
|
// If some other error occured handle it outside the loop
|
|
//
|
|
if (status == STATUS_IO_TIMEOUT) {
|
|
KeStallExecutionProcessor(50);
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xFindWheelMouse(
|
|
IN PPORT_MOUSE_EXTENSION MouseExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
There are two methods of finding a wheel mouse on a system. The first
|
|
method, is to send down the request to get the PNP id of the device
|
|
and compare it with the known id for a wheel mouse. The method is
|
|
useful since some machines hang on the second detection mechanism,
|
|
even if no mouse is present on the system.
|
|
|
|
The second method, which also enables a wheel mouse is set the sampling
|
|
rate to 200hz, then 100hz, then 80hz, and then read the device id. An
|
|
ID of 3 indicates a zoom mouse.
|
|
|
|
If the registry entry "EnableWheelDetection" is 0 then this
|
|
routine will just return STATUS_NO_SUCH_DEVICE. If the registry entry
|
|
is 1 (the default), then the first and second detection mechanisms will
|
|
be used. If the registry entry is 2, then only the second detection
|
|
mechanism will be used.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object
|
|
|
|
Return Value:
|
|
|
|
Returns status
|
|
|
|
Remarks:
|
|
|
|
As a side effect the sample rate is left at 80Hz and if a wheelmouse is
|
|
attached it is in the wheel mode where packets are different.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define DUMP_COUNT 4
|
|
NTSTATUS errorCode = STATUS_SUCCESS;
|
|
NTSTATUS status;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
UCHAR byte;
|
|
UCHAR enableCommands[] = {
|
|
SET_MOUSE_SAMPLING_RATE, 200,
|
|
SET_MOUSE_SAMPLING_RATE, 100,
|
|
SET_MOUSE_SAMPLING_RATE, 80,
|
|
GET_DEVICE_ID, 0 // NULL terminate
|
|
};
|
|
UCHAR enable5Commands[] = {
|
|
SET_MOUSE_SAMPLING_RATE, 200,
|
|
SET_MOUSE_SAMPLING_RATE, 200,
|
|
SET_MOUSE_SAMPLING_RATE, 80,
|
|
GET_DEVICE_ID, 0 // NULL terminate
|
|
};
|
|
UCHAR pnpCommands[] = {
|
|
SET_MOUSE_SAMPLING_RATE, 20,
|
|
SET_MOUSE_SAMPLING_RATE, 40,
|
|
SET_MOUSE_SAMPLING_RATE, 60,
|
|
0 // NULL terminates
|
|
};
|
|
ULONG dumpCount = 0;
|
|
ULONG dumpData[DUMP_COUNT];
|
|
ULONG i;
|
|
ULONG idCount;
|
|
ULONG uniqueErrorValue = I8042_ERROR_VALUE_BASE + 480;
|
|
WCHAR mouseID[MOUSE_PNPID_LENGTH];
|
|
PWCHAR currentChar;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Let the world know that we have entered this routine
|
|
//
|
|
Print(DBG_SS_TRACE,
|
|
("%s, %s\n",
|
|
pFncFindWheelMouse,
|
|
pEnter
|
|
));
|
|
|
|
if (MouseExtension->EnableWheelDetection == 0) {
|
|
|
|
Print(DBG_SS_INFO | DBG_SS_NOISE,
|
|
("%s: Detection disabled in registry\n",
|
|
pFncFindWheelMouse
|
|
));
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Initialize some variables
|
|
//
|
|
for(i = 0; i < DUMP_COUNT; i++) {
|
|
dumpData[i] = 0;
|
|
}
|
|
|
|
//
|
|
// If the MouseInterruptObject exists, then we have gone through initialization
|
|
// at least once and know about the mouse attached
|
|
//
|
|
if (MouseExtension->InterruptObject) {
|
|
if (WHEEL_PRESENT()) {
|
|
//
|
|
// Skip detection and go straight to turning on the wheel
|
|
//
|
|
goto InitializeWheel;
|
|
}
|
|
else {
|
|
//
|
|
// No wheel mouse present, no need to detect it a second time
|
|
//
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// What is the point of this here???
|
|
//
|
|
KeStallExecutionProcessor(50);
|
|
|
|
//
|
|
// First check to see if we will try the 'better' method of detection
|
|
//
|
|
if (MouseExtension->EnableWheelDetection == 1) {
|
|
|
|
status = I8xTransmitByteSequence(
|
|
pnpCommands,
|
|
&uniqueErrorValue,
|
|
&errorCode,
|
|
dumpData,
|
|
&dumpCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto I8xFindWheelMouseExit;
|
|
}
|
|
|
|
//
|
|
// Zero out the string that will ID the mouse
|
|
//
|
|
RtlZeroMemory(mouseID,
|
|
MOUSE_PNPID_LENGTH * sizeof(WCHAR)
|
|
);
|
|
|
|
currentChar = mouseID;
|
|
|
|
//
|
|
// We should start to see the PNP string come back our way
|
|
// (MOUSE_PNPID_LENGTH includes the NULL in its length)
|
|
//
|
|
for (idCount = 0; idCount < MOUSE_PNPID_LENGTH-1; idCount++) {
|
|
status = I8xGetBytePolledIterated(
|
|
(CCHAR) ControllerDeviceType,
|
|
&byte,
|
|
5
|
|
);
|
|
|
|
|
|
//
|
|
// if the operation wasn't successful or the characters don't
|
|
// match, than try to flush the buffers
|
|
//
|
|
if (byte < ScanCodeToUCharCount) {
|
|
*currentChar = ScanCodeToUChar[byte];
|
|
if (*currentChar) {
|
|
currentChar++;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) { // || byte != pnpID[idCount]) {
|
|
//
|
|
// Couldn't get a byte
|
|
//
|
|
do {
|
|
//
|
|
// Wait a little bit
|
|
//
|
|
KeStallExecutionProcessor( 50 );
|
|
|
|
//
|
|
// Get a byte if there is one
|
|
//
|
|
status = I8xGetBytePolled(
|
|
(CCHAR) ControllerDeviceType,
|
|
&byte
|
|
);
|
|
} while (status != STATUS_IO_TIMEOUT);
|
|
|
|
//
|
|
// We are done here
|
|
//
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
} // if
|
|
} // for
|
|
|
|
Print(DBG_SS_INFO, ("found a pnp id of %ws\n", mouseID));
|
|
if (!I8xVerifyMousePnPID(MouseExtension, mouseID)) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
}
|
|
else if (MouseExtension->EnableWheelDetection != 2) {
|
|
//
|
|
// We got a bogus id. Let's just assume that they meant to disable
|
|
// the little detection routine
|
|
//
|
|
Print(DBG_SS_INFO | DBG_SS_NOISE,
|
|
("%s: Detection disabled in registry\n",
|
|
pFncFindWheelMouse
|
|
));
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
|
|
} // if
|
|
|
|
//
|
|
// Start the second detection routine, which will also enable the
|
|
// device if present
|
|
//
|
|
InitializeWheel:
|
|
status = I8xTransmitByteSequence(
|
|
enableCommands,
|
|
&uniqueErrorValue,
|
|
&errorCode,
|
|
dumpData,
|
|
&dumpCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto I8xFindWheelMouseExit;
|
|
}
|
|
|
|
//
|
|
// Get the mouse ID
|
|
//
|
|
|
|
status = I8xGetBytePolledIterated(
|
|
(CCHAR) ControllerDeviceType,
|
|
&byte,
|
|
5
|
|
);
|
|
|
|
//
|
|
// Check to see what we got
|
|
//
|
|
if ((!NT_SUCCESS(status)) ||
|
|
((byte != MOUSE_ID_BYTE) && (byte != WHEELMOUSE_ID_BYTE))) {
|
|
Print(DBG_SS_ERROR,
|
|
("%s, failed ID, status 0x%x, byte 0x%x\n",
|
|
pFncFindWheelMouse,
|
|
status,
|
|
byte
|
|
));
|
|
|
|
//
|
|
// Set up error log info
|
|
//
|
|
errorCode = I8042_MOU_RESET_RESPONSE_FAILED;
|
|
dumpData[0] = KBDMOU_INCORRECT_RESPONSE;
|
|
dumpData[1] = ControllerDeviceType;
|
|
dumpData[2] = MOUSE_ID_BYTE;
|
|
dumpData[3] = byte;
|
|
dumpCount = 4;
|
|
goto I8xFindWheelMouseExit;
|
|
}
|
|
else if (byte == WHEELMOUSE_ID_BYTE) {
|
|
//
|
|
// Update the HardwarePresent to show a Z mouse is operational,
|
|
// and set the appropriate mouse type flags
|
|
//
|
|
SET_HW_FLAGS(WHEELMOUSE_HARDWARE_PRESENT);
|
|
|
|
MouseExtension->MouseAttributes.MouseIdentifier =
|
|
WHEELMOUSE_I8042_HARDWARE;
|
|
|
|
status = I8xTransmitByteSequence(
|
|
enable5Commands,
|
|
&uniqueErrorValue,
|
|
&errorCode,
|
|
dumpData,
|
|
&dumpCount
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = I8xGetBytePolledIterated(
|
|
(CCHAR) ControllerDeviceType,
|
|
&byte,
|
|
5
|
|
);
|
|
|
|
if (NT_SUCCESS(status) && byte == FIVEBUTTON_ID_BYTE) {
|
|
//
|
|
// Update the HardwarePresent to show a Z mouse with 2 extra buttons is operational,
|
|
// and set the appropriate mouse type flags
|
|
//
|
|
SET_HW_FLAGS(FIVE_BUTTON_HARDWARE_PRESENT | WHEELMOUSE_HARDWARE_PRESENT);
|
|
|
|
MouseExtension->MouseAttributes.MouseIdentifier =
|
|
WHEELMOUSE_I8042_HARDWARE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
SET_HW_FLAGS(MOUSE_HARDWARE_PRESENT);
|
|
|
|
Print(DBG_SS_INFO,
|
|
("%s, Mouse attached - running in mouse mode.\n",
|
|
pFncFindWheelMouse
|
|
));
|
|
}
|
|
|
|
I8xFindWheelMouseExit:
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// The mouse initialization failed. Log an error.
|
|
//
|
|
if(errorCode != STATUS_SUCCESS) {
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(
|
|
MouseExtension->Self,
|
|
(UCHAR) (sizeof(IO_ERROR_LOG_PACKET) +
|
|
(dumpCount * sizeof(ULONG)))
|
|
);
|
|
|
|
if(errorLogEntry != NULL) {
|
|
|
|
errorLogEntry->ErrorCode = errorCode;
|
|
errorLogEntry->DumpDataSize = (USHORT) dumpCount * sizeof(ULONG);
|
|
errorLogEntry->SequenceNumber = 0;
|
|
errorLogEntry->MajorFunctionCode = 0;
|
|
errorLogEntry->IoControlCode = 0;
|
|
errorLogEntry->RetryCount = 0;
|
|
errorLogEntry->UniqueErrorValue = uniqueErrorValue;
|
|
errorLogEntry->FinalStatus = status;
|
|
for(i = 0; i < dumpCount; i++) {
|
|
|
|
errorLogEntry->DumpData[i] = dumpData[i];
|
|
|
|
}
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Print(DBG_SS_TRACE, ("FindWheel mouse (0x%x)\n", status));
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
I8xFinishResetRequest(
|
|
PPORT_MOUSE_EXTENSION MouseExtension,
|
|
BOOLEAN Failed,
|
|
BOOLEAN RaiseIrql,
|
|
BOOLEAN CancelTimer
|
|
)
|
|
{
|
|
PIRP irp;
|
|
KIRQL oldIrql;
|
|
|
|
irp = (PIRP) InterlockedExchangePointer(&MouseExtension->ResetIrp,
|
|
NULL
|
|
);
|
|
|
|
if (CancelTimer) {
|
|
//
|
|
// We must cancel our watchdog timer so that it doesn't try to reset the
|
|
// mouse at a later time
|
|
//
|
|
KeCancelTimer(&MouseExtension->ResetMouse.Timer);
|
|
}
|
|
|
|
Print(DBG_IOCTL_INFO | DBG_SS_INFO,
|
|
("Finished with mouse reset irp %p\n", irp));
|
|
|
|
//
|
|
// Raise to dispatch because KeInsertQueueDpc, IoFreeController, and
|
|
// IoStartNextPacket all require to be at this irql.
|
|
//
|
|
if (RaiseIrql) {
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
}
|
|
|
|
//
|
|
// Let people know that the reset failed
|
|
//
|
|
if (Failed && Globals.ReportResetErrors) {
|
|
KeInsertQueueDpc(&MouseExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_MOU_RESET_RESPONSE_FAILED)
|
|
);
|
|
}
|
|
|
|
CLEAR_RECORD_STATE(MouseExtension);
|
|
|
|
//
|
|
// NOTE: To prevent the reset detection code from
|
|
// restarting us all over again (which don't want
|
|
// otherwise we would not be here, we need to fool
|
|
// the detection into thinking that the last character
|
|
// wasn't MOUSE_COMPLETE
|
|
//
|
|
MouseExtension->LastByteReceived = 0;
|
|
|
|
//
|
|
// Oops. Oh well, the mouse hasn't been able to reset
|
|
// in all these tries, so lets consider it dead.
|
|
//
|
|
// However, just in case the user yanks it out and
|
|
// plugs in a new one, we should reset our count
|
|
// back down to zero so that we will actually try to
|
|
// activate the thing when he plugs it back in there....but we don't do
|
|
// this here. If we see the reset sequence in the ISR, we will reset
|
|
// the counts there.
|
|
//
|
|
//MouseExtension->ResendCount = 0;
|
|
|
|
// I8X_MOUSE_INIT_COUNTERS(MouseExtension);
|
|
|
|
//
|
|
// Make sure the next packet is started, regardless if the reset IRP
|
|
// was present or not
|
|
//
|
|
IoFreeController(Globals.ControllerData->ControllerObject);
|
|
IoStartNextPacket(MouseExtension->Self, FALSE);
|
|
|
|
if (RaiseIrql) {
|
|
KeLowerIrql(oldIrql);
|
|
}
|
|
|
|
if (irp != NULL) {
|
|
IoFreeIrp(irp);
|
|
IoReleaseRemoveLock(&MouseExtension->RemoveLock, irp);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
I8xResetMouseFailed(
|
|
PPORT_MOUSE_EXTENSION MouseExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The resetting of the mouse failed after repeated tries to get it working.
|
|
Free the irp and start the next packet in our start io reoutine.
|
|
|
|
Arguments:
|
|
|
|
MouseExtension - Mouse extension
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
KIRQL oldIrql;
|
|
|
|
Print(DBG_SS_ERROR | DBG_SS_INFO, ("mouse reset failed\n"));
|
|
|
|
//
|
|
// Mark the failed reset in the device extension
|
|
//
|
|
MouseExtension->ResetMouse.IsrResetState = MouseResetFailed;
|
|
|
|
I8xFinishResetRequest(MouseExtension, TRUE, TRUE, TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xResetMouse(
|
|
PPORT_MOUSE_EXTENSION MouseExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends the reset command to the mouse (through the start i/o routine if it
|
|
doesn't exist yet) if we haven't reached our reset limit. Otherwise, gives
|
|
up and calls I8xResetMouseFailed.
|
|
|
|
Arguments:
|
|
|
|
MouseExtension - Mouse extension
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT self;
|
|
PIO_STACK_LOCATION stack;
|
|
PIRP pResetIrp, pIrp;
|
|
NTSTATUS status;
|
|
|
|
Print(DBG_SS_NOISE, ("reset count = %d\n", (LONG) MouseExtension->ResetCount));
|
|
|
|
self = MouseExtension->Self;
|
|
status = STATUS_SUCCESS;
|
|
|
|
MouseExtension->ResetCount++;
|
|
MouseExtension->FailedCompleteResetCount++;
|
|
|
|
if (MouseExtension->ResetCount >= MOUSE_RESETS_MAX ||
|
|
MouseExtension->FailedCompleteResetCount >= MOUSE_RESETS_MAX) {
|
|
Print(DBG_SS_ERROR, ("Resetting mouse failed!\n"));
|
|
I8xResetMouseFailed(MouseExtension);
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
#if 0
|
|
if (MouseExtension->LastByteReceived == 0xFC &&
|
|
MouseExtension->InputState == MouseResetting) {
|
|
I8xDrainOutputBuffer(
|
|
Globals.ControllerData->DeviceRegisters[DataPort],
|
|
Globals.ControllerData->DeviceRegisters[CommandPort]
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Insert a "fake" request into the StartIO queue for the reset. This way,
|
|
// the reset of the mouse can be serialized with all of the other kb IOCTLS
|
|
// that come down during start or return from a low power state
|
|
//
|
|
pResetIrp = IoAllocateIrp(self->StackSize, FALSE);
|
|
if (pResetIrp == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pIrp = (PIRP) InterlockedCompareExchangePointer(&MouseExtension->ResetIrp,
|
|
pResetIrp,
|
|
NULL);
|
|
|
|
//
|
|
// Check to see if we had a pending reset request. If there was,
|
|
// pIrp != NULL and we should just write the reset to the device now.
|
|
//
|
|
if (pIrp == NULL) {
|
|
stack = IoGetNextIrpStackLocation(pResetIrp);
|
|
stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_MOUSE_RESET;
|
|
IoSetNextIrpStackLocation(pResetIrp);
|
|
|
|
status = IoAcquireRemoveLock(&MouseExtension->RemoveLock, pResetIrp);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
Print(DBG_SS_INFO, ("IoStarting reset irp %p\n", pResetIrp));
|
|
IoStartPacket(self, pResetIrp, (PULONG) NULL, NULL);
|
|
}
|
|
else {
|
|
pIrp = (PIRP) InterlockedExchangePointer(&MouseExtension->ResetIrp,
|
|
NULL);
|
|
|
|
Print(DBG_SS_INFO, ("Failed acquire on reset irp %p\n", pIrp));
|
|
|
|
if (pIrp != NULL) {
|
|
ASSERT(pIrp == pResetIrp);
|
|
IoFreeIrp(pIrp);
|
|
pIrp = NULL;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Free the irp we just allocated
|
|
//
|
|
IoFreeIrp(pResetIrp);
|
|
pResetIrp = NULL;
|
|
|
|
//
|
|
// The Reset Irp exists, just send another reset
|
|
//
|
|
I8xSendResetCommand(MouseExtension);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
I8xSendResetCommand (
|
|
PPORT_MOUSE_EXTENSION MouseExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes the actual reset to the mouse and kicks off the watch dog timer.
|
|
|
|
Arguments:
|
|
|
|
MouseExtension - Mouse extension
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
LARGE_INTEGER li = RtlConvertLongToLargeInteger(-MOUSE_RESET_TIMEOUT);
|
|
|
|
MouseExtension->ResetMouse.IsrResetState = IsrResetNormal;
|
|
|
|
//
|
|
// Delay for 1 second
|
|
//
|
|
KeSetTimer(&MouseExtension->ResetMouse.Timer,
|
|
li,
|
|
&MouseExtension->ResetMouse.Dpc
|
|
);
|
|
|
|
MouseExtension->PreviousSignAndOverflow = 0;
|
|
MouseExtension->InputState = MouseResetting;
|
|
MouseExtension->InputResetSubState = ExpectingReset;
|
|
MouseExtension->LastByteReceived = 0;
|
|
|
|
//
|
|
// The watch dog timer bases its time computations on this value, set it to
|
|
// now so that all the times computed are relative to the last time we sent
|
|
// a reset and not x seconds ago when we last received an interrupt from the
|
|
// mouse
|
|
//
|
|
KeQueryTickCount(&MouseExtension->PreviousTick);
|
|
|
|
I8xPutBytePolled((CCHAR) DataPort,
|
|
NO_WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) MouseDeviceType,
|
|
(UCHAR) MOUSE_RESET
|
|
);
|
|
}
|
|
|
|
VOID
|
|
I8xQueueCurrentMouseInput(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queues the current input data to be processed by a
|
|
DPC outside the ISR
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PPORT_MOUSE_EXTENSION deviceExtension;
|
|
UCHAR buttonsDelta;
|
|
UCHAR previousButtons;
|
|
|
|
deviceExtension = (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// If the mouse is enabled, add the data to the InputData queue
|
|
// and queue the ISR DPC. One might wonder why we bother to
|
|
// do all this processing of the mouse packet, only to toss it
|
|
// away (i.e., not queue it) at this point. The answer is that
|
|
// this mouse provides no data to allow the driver to determine
|
|
// when the first byte of a packet is received -- if the driver
|
|
// doesn't process all interrupts from the start, there is no
|
|
// way to keep MouseExtension.InputState in synch with hardware
|
|
// reality.
|
|
//
|
|
if (deviceExtension->EnableCount) {
|
|
|
|
if (!I8xWriteDataToMouseQueue(
|
|
deviceExtension,
|
|
&deviceExtension->CurrentInput
|
|
)) {
|
|
|
|
//
|
|
// InputData queue overflowed.
|
|
//
|
|
// Queue a DPC to log an overrun error.
|
|
//
|
|
IsrPrint(DBG_MOUISR_ERROR,
|
|
("I8042MouseInterruptService: queue overflow\n"
|
|
));
|
|
|
|
if (deviceExtension->OkayToLogOverflow) {
|
|
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_MOU_BUFFER_OVERFLOW)
|
|
);
|
|
deviceExtension->OkayToLogOverflow =
|
|
FALSE;
|
|
|
|
}
|
|
|
|
} else if (deviceExtension->DpcInterlockMouse >= 0) {
|
|
|
|
//
|
|
// The ISR DPC is already executing. Tell the ISR DPC it has
|
|
// more work to do by incrementing DpcInterlockMouse.
|
|
//
|
|
deviceExtension->DpcInterlockMouse += 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Queue the ISR DPC.
|
|
//
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->MouseIsrDpc,
|
|
(PIRP) NULL, // DeviceObject->CurrentIrp,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MouseCopyWheelIDs(
|
|
OUT PUNICODE_STRING Destination,
|
|
IN PUNICODE_STRING Source
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies the multisz specified in Source into Destinatio along with the default
|
|
IDs already known. Destination is in non paged pool while Source is paged.
|
|
|
|
Argument:
|
|
|
|
Destination - Will receive new copied string
|
|
|
|
Source - String read from the registry
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PWSTR str = NULL;
|
|
ULONG length;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Destination->Buffer == NULL);
|
|
|
|
RtlZeroMemory(Destination, sizeof(*Destination));
|
|
|
|
//
|
|
// Check to see the Source string is not just an empty multi SZ
|
|
//
|
|
if (Source->MaximumLength > (sizeof(L'\0') * 2)) {
|
|
Destination->Buffer = (WCHAR*)
|
|
ExAllocatePool(NonPagedPool, Source->MaximumLength * sizeof(WCHAR));
|
|
|
|
if (Destination->Buffer != NULL) {
|
|
RtlCopyMemory(Destination->Buffer,
|
|
Source->Buffer,
|
|
Source->MaximumLength * sizeof(WCHAR));
|
|
|
|
Destination->Length = Destination->MaximumLength =
|
|
Source->MaximumLength;
|
|
|
|
//
|
|
// Make sure each string is in upper case
|
|
//
|
|
str = Destination->Buffer;
|
|
while (*str != L'\0') {
|
|
Print(DBG_SS_NOISE, ("wheel id: %ws\n", str));
|
|
_wcsupr(str);
|
|
str += wcslen(str) + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
I8xMouseServiceParameters(
|
|
IN PUNICODE_STRING RegistryPath,
|
|
IN PPORT_MOUSE_EXTENSION MouseExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves this driver's service parameters information
|
|
from the registry. Overrides these values if they are present in the
|
|
devnode.
|
|
|
|
Arguments:
|
|
|
|
RegistryPath - Pointer to the null-terminated Unicode name of the
|
|
registry path for this driver.
|
|
|
|
MouseExtension - Mouse extension
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
|
|
HANDLE keyHandle;
|
|
UNICODE_STRING parametersPath;
|
|
PWSTR path = NULL;
|
|
ULONG defaultDataQueueSize = DATA_QUEUE_SIZE,
|
|
defaultSynchPacket100ns = MOUSE_SYNCH_PACKET_100NS,
|
|
defaultEnableWheelDetection = 1,
|
|
defaultMouseResolution = MOUSE_RESOLUTION,
|
|
defaultNumberOfButtons = 0,
|
|
defaultSampleRate = MOUSE_SAMPLE_RATE,
|
|
defaultWheelDetectionTimeout = WHEEL_DETECTION_TIMEOUT,
|
|
defaultInitializePolled = I8X_INIT_POLLED_DEFAULT,
|
|
enableWheelDetection = 1,
|
|
mouseResolution = MOUSE_RESOLUTION,
|
|
numberOfButtons = MOUSE_NUMBER_OF_BUTTONS,
|
|
sampleRate = MOUSE_SAMPLE_RATE,
|
|
initializePolled = I8X_INIT_POLLED_DEFAULT,
|
|
i = 0;
|
|
ULONG defaultStallTime = 1000;
|
|
LARGE_INTEGER largeDetectionTimeout;
|
|
USHORT queries = 10;
|
|
|
|
WCHAR szDefaultIDs[] = { L"\0" };
|
|
UNICODE_STRING IDs;
|
|
|
|
#if MOUSE_RECORD_ISR
|
|
ULONG defaultHistoryLength = 100,
|
|
defaultRecordHistoryFlags = 0x0;
|
|
|
|
queries += 2;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
parametersPath.Buffer = NULL;
|
|
|
|
//
|
|
// Registry path is already null-terminated, so just use it.
|
|
//
|
|
path = RegistryPath->Buffer;
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Allocate the Rtl query table.
|
|
//
|
|
parameters = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1)
|
|
);
|
|
|
|
if (!parameters) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: couldn't allocate table for Rtl query to %ws for %ws\n",
|
|
pFncServiceParameters,
|
|
pwParameters,
|
|
path
|
|
));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory(
|
|
parameters,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1)
|
|
);
|
|
|
|
//
|
|
// Form a path to this driver's Parameters subkey.
|
|
//
|
|
RtlInitUnicodeString( ¶metersPath, NULL );
|
|
parametersPath.MaximumLength = RegistryPath->Length +
|
|
(wcslen(pwParameters) * sizeof(WCHAR) ) + sizeof(UNICODE_NULL);
|
|
|
|
parametersPath.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
parametersPath.MaximumLength
|
|
);
|
|
|
|
if (!parametersPath.Buffer) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: Couldn't allocate string for path to %ws for %ws\n",
|
|
pFncServiceParameters,
|
|
pwParameters,
|
|
path
|
|
));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Form the parameters path.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
parametersPath.Buffer,
|
|
parametersPath.MaximumLength
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
¶metersPath,
|
|
path
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
¶metersPath,
|
|
pwParameters
|
|
);
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry.
|
|
//
|
|
parameters[i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwMouseDataQueueSize;
|
|
parameters[i].EntryContext =
|
|
&MouseExtension->MouseAttributes.InputDataQueueLength;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultDataQueueSize;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwNumberOfButtons;
|
|
parameters[i].EntryContext = &numberOfButtons;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultNumberOfButtons;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwSampleRate;
|
|
parameters[i].EntryContext = &sampleRate;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultSampleRate;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwMouseResolution;
|
|
parameters[i].EntryContext = &mouseResolution;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultMouseResolution;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwMouseSynchIn100ns;
|
|
parameters[i].EntryContext = &MouseExtension->SynchTickCount;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
|
|
parameters[i].DefaultData = &defaultSynchPacket100ns;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwEnableWheelDetection;
|
|
parameters[i].EntryContext = &enableWheelDetection;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultEnableWheelDetection;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = L"MouseInitializePolled";
|
|
parameters[i].EntryContext = &initializePolled;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultInitializePolled;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = L"MouseResendStallTime";
|
|
parameters[i].EntryContext = &MouseExtension->MouseResetStallTime;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultStallTime;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
#if MOUSE_RECORD_ISR
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = L"RecordMouseIsrFlags";
|
|
parameters[i].EntryContext = &MouseExtension->RecordHistoryFlags;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultRecordHistoryFlags;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = L"RecordMouseIsrLength";
|
|
parameters[i].EntryContext = &MouseExtension->RecordHistoryCount;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultHistoryLength;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
#endif
|
|
|
|
status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
parametersPath.Buffer,
|
|
parameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_INFO,
|
|
("mou RtlQueryRegistryValues failed (0x%x)\n",
|
|
status
|
|
));
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Go ahead and assign driver defaults.
|
|
//
|
|
MouseExtension->MouseAttributes.InputDataQueueLength =
|
|
defaultDataQueueSize;
|
|
MouseExtension->EnableWheelDetection = (UCHAR)
|
|
defaultEnableWheelDetection;
|
|
MouseExtension->SynchTickCount = defaultSynchPacket100ns;
|
|
}
|
|
|
|
Print(DBG_SS_NOISE,
|
|
("results from services key:\n"
|
|
"\tmouse queue length = %d\n"
|
|
"\tnumber of buttons = %d\n"
|
|
"\tsample rate = %d\n"
|
|
"\tresolution = %d\n"
|
|
"\tsynch tick count = %d\n"
|
|
"\twheel detection = %d\n"
|
|
"\tdetection timeout = %d\n"
|
|
"\tintiailize polled = %d\n"
|
|
"\treset stall time = %d\n",
|
|
MouseExtension->MouseAttributes.InputDataQueueLength,
|
|
numberOfButtons,
|
|
sampleRate,
|
|
mouseResolution,
|
|
MouseExtension->SynchTickCount,
|
|
enableWheelDetection,
|
|
MouseExtension->WheelDetectionTimeout,
|
|
initializePolled,
|
|
MouseExtension->MouseResetStallTime
|
|
));
|
|
|
|
status = IoOpenDeviceRegistryKey(MouseExtension->PDO,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_READ,
|
|
&keyHandle
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
ULONG prevInputDataQueueLength,
|
|
prevNumberOfButtons,
|
|
prevSampleRate,
|
|
prevMouseResolution,
|
|
prevSynchPacket100ns,
|
|
prevEnableWheelDetection,
|
|
prevWheelDetectionTimeout,
|
|
prevInitializePolled;
|
|
|
|
RtlInitUnicodeString(&IDs,
|
|
NULL);
|
|
|
|
//
|
|
// If the value is not present in devnode, then the default is the value
|
|
// read in from the Services\i8042prt\Parameters key
|
|
//
|
|
prevInputDataQueueLength =
|
|
MouseExtension->MouseAttributes.InputDataQueueLength;
|
|
prevNumberOfButtons = numberOfButtons;
|
|
prevSampleRate = sampleRate;
|
|
prevMouseResolution = mouseResolution;
|
|
prevSynchPacket100ns = MouseExtension->SynchTickCount;
|
|
prevEnableWheelDetection = enableWheelDetection;
|
|
prevWheelDetectionTimeout = MouseExtension->WheelDetectionTimeout;
|
|
prevInitializePolled = initializePolled;
|
|
|
|
i = 0;
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry (this time from the devnode)
|
|
//
|
|
parameters[i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwMouseDataQueueSize;
|
|
parameters[i].EntryContext =
|
|
&MouseExtension->MouseAttributes.InputDataQueueLength;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevInputDataQueueLength;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwNumberOfButtons;
|
|
parameters[i].EntryContext = &numberOfButtons;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevNumberOfButtons;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwSampleRate;
|
|
parameters[i].EntryContext = &sampleRate;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevSampleRate;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwMouseResolution;
|
|
parameters[i].EntryContext = &mouseResolution;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevMouseResolution;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwMouseSynchIn100ns;
|
|
parameters[i].EntryContext = &MouseExtension->SynchTickCount;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevSynchPacket100ns;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwEnableWheelDetection;
|
|
parameters[i].EntryContext = &enableWheelDetection;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevEnableWheelDetection;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = L"MouseInitializePolled";
|
|
parameters[i].EntryContext = &initializePolled;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevInitializePolled;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND;
|
|
parameters[i].Name = L"WheelDetectIDs";
|
|
parameters[i].EntryContext = &IDs;
|
|
parameters[i].DefaultType = REG_MULTI_SZ;
|
|
parameters[i].DefaultData = szDefaultIDs;
|
|
parameters[i].DefaultLength = sizeof(szDefaultIDs);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = L"WheelDetectionTimeout";
|
|
parameters[i].EntryContext = &MouseExtension->WheelDetectionTimeout;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultWheelDetectionTimeout;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_HANDLE,
|
|
(PWSTR) keyHandle,
|
|
parameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
Print(DBG_SS_NOISE,
|
|
("results from devnode key:\n"
|
|
"\tmouse queue length = %d\n"
|
|
"\tnumber of buttons = %d\n"
|
|
"\tsample rate = %d\n"
|
|
"\tresolution = %d\n"
|
|
"\tsynch tick count = %d\n"
|
|
"\twheel detection = %d\n"
|
|
"\tinitialize polled = %d\n"
|
|
"\tdetection timeout = %d\n",
|
|
MouseExtension->MouseAttributes.InputDataQueueLength,
|
|
numberOfButtons,
|
|
sampleRate,
|
|
mouseResolution,
|
|
MouseExtension->SynchTickCount,
|
|
enableWheelDetection,
|
|
initializePolled,
|
|
MouseExtension->WheelDetectionTimeout
|
|
));
|
|
}
|
|
else {
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("mou RtlQueryRegistryValues (via handle) failed with (0x%x)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
ZwClose(keyHandle);
|
|
}
|
|
else {
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("mou, opening devnode handle failed (0x%x)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
//
|
|
// Needs to be in NonPagedPool so it can be access during the ISR
|
|
//
|
|
MouseCopyWheelIDs(&MouseExtension->WheelDetectionIDs,
|
|
&IDs);
|
|
RtlFreeUnicodeString(&IDs);
|
|
|
|
Print(DBG_SS_NOISE, ("I8xMouseServiceParameters results..\n"));
|
|
|
|
if (MouseExtension->MouseAttributes.InputDataQueueLength == 0) {
|
|
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("\toverriding %ws = 0x%x\n",
|
|
pwMouseDataQueueSize,
|
|
MouseExtension->MouseAttributes.InputDataQueueLength
|
|
));
|
|
|
|
MouseExtension->MouseAttributes.InputDataQueueLength =
|
|
defaultDataQueueSize;
|
|
}
|
|
MouseExtension->MouseAttributes.InputDataQueueLength *=
|
|
sizeof(MOUSE_INPUT_DATA);
|
|
|
|
MouseExtension->InitializePolled = (UCHAR) initializePolled;
|
|
|
|
switch (enableWheelDetection) {
|
|
case 2:
|
|
case 1:
|
|
MouseExtension->EnableWheelDetection = (UCHAR) enableWheelDetection;
|
|
break;
|
|
default:
|
|
MouseExtension->EnableWheelDetection = 0;
|
|
}
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpHex,
|
|
pwEnableWheelDetection,
|
|
MouseExtension->EnableWheelDetection
|
|
));
|
|
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpHex,
|
|
pwMouseDataQueueSize,
|
|
MouseExtension->MouseAttributes.InputDataQueueLength
|
|
));
|
|
|
|
if (numberOfButtons == 0) {
|
|
MouseExtension->NumberOfButtonsOverride = 0;
|
|
MouseExtension->MouseAttributes.NumberOfButtons = MOUSE_NUMBER_OF_BUTTONS;
|
|
}
|
|
else {
|
|
MouseExtension->NumberOfButtonsOverride = (UCHAR) numberOfButtons;
|
|
MouseExtension->MouseAttributes.NumberOfButtons = (USHORT) numberOfButtons;
|
|
}
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
pwNumberOfButtons,
|
|
MouseExtension->MouseAttributes.NumberOfButtons
|
|
));
|
|
|
|
MouseExtension->MouseAttributes.SampleRate = (USHORT) sampleRate;
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
pwSampleRate,
|
|
MouseExtension->MouseAttributes.SampleRate
|
|
));
|
|
|
|
MouseExtension->Resolution = (UCHAR) mouseResolution;
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
pwMouseResolution,
|
|
mouseResolution
|
|
));
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
L"MouseResetStallTime",
|
|
MouseExtension->MouseResetStallTime
|
|
));
|
|
|
|
if (MouseExtension->WheelDetectionTimeout > 4000) {
|
|
MouseExtension->WheelDetectionTimeout = WHEEL_DETECTION_TIMEOUT;
|
|
}
|
|
//
|
|
// Convert ms to 100 ns units
|
|
// 1000 => ms to us
|
|
// 10 => us to 100 ns
|
|
//
|
|
largeDetectionTimeout.QuadPart = MouseExtension->WheelDetectionTimeout *
|
|
1000 * 10;
|
|
largeDetectionTimeout.QuadPart /= KeQueryTimeIncrement();
|
|
MouseExtension->WheelDetectionTimeout = largeDetectionTimeout.LowPart;
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
L"WheelDetectionTimeout",
|
|
MouseExtension->WheelDetectionTimeout
|
|
));
|
|
|
|
|
|
if (MouseExtension->SynchTickCount == 0) {
|
|
|
|
Print(DBG_SS_ERROR | DBG_SS_INFO,
|
|
("\toverriding %ws\n",
|
|
pwMouseSynchIn100ns
|
|
));
|
|
|
|
MouseExtension->SynchTickCount = defaultSynchPacket100ns;
|
|
|
|
}
|
|
|
|
//
|
|
// Convert SynchTickCount to be the number of interval timer
|
|
// interrupts that occur during the time specified by MouseSynchIn100ns.
|
|
// Note that KeQueryTimeIncrement returns the number of 100ns units that
|
|
// are added to the system time each time the interval clock interrupts.
|
|
//
|
|
MouseExtension->SynchTickCount /= KeQueryTimeIncrement();
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpHex,
|
|
pwMouseSynchIn100ns,
|
|
MouseExtension->SynchTickCount
|
|
));
|
|
|
|
//
|
|
// Free the allocated memory before returning.
|
|
//
|
|
if (parametersPath.Buffer)
|
|
ExFreePool(parametersPath.Buffer);
|
|
|
|
if (parameters)
|
|
ExFreePool(parameters);
|
|
}
|