/*++
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