415 lines
13 KiB
C
415 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
moucmn.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
The common portions of the Intel i8042 port driver which
|
|||
|
apply 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"
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
|
|||
|
#if 1
|
|||
|
#pragma alloc_text(PAGEMOUC, I8042MouseIsrDpc)
|
|||
|
#pragma alloc_text(PAGEMOUC, I8xWriteDataToMouseQueue)
|
|||
|
#endif
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
I8042MouseIsrDpc(
|
|||
|
IN PKDPC Dpc,
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine runs at DISPATCH_LEVEL IRQL to finish processing
|
|||
|
mouse interrupts. It is queued in the mouse ISR. The real
|
|||
|
work is done via a callback to the connected mouse class driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Pointer to the DPC object.
|
|||
|
|
|||
|
DeviceObject - Pointer to the device object.
|
|||
|
|
|||
|
Irp - Pointer to the Irp.
|
|||
|
|
|||
|
Context - Not used.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PPORT_MOUSE_EXTENSION deviceExtension;
|
|||
|
GET_DATA_POINTER_CONTEXT getPointerContext;
|
|||
|
SET_DATA_POINTER_CONTEXT setPointerContext;
|
|||
|
VARIABLE_OPERATION_CONTEXT operationContext;
|
|||
|
PVOID classService;
|
|||
|
PVOID classDeviceObject;
|
|||
|
LONG interlockedResult;
|
|||
|
BOOLEAN moreDpcProcessing;
|
|||
|
ULONG dataNotConsumed = 0;
|
|||
|
ULONG inputDataConsumed = 0;
|
|||
|
LARGE_INTEGER deltaTime;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(Dpc);
|
|||
|
UNREFERENCED_PARAMETER(Irp);
|
|||
|
UNREFERENCED_PARAMETER(Context);
|
|||
|
|
|||
|
Print(DBG_DPC_TRACE, ("I8042MouseIsrDpc: enter\n"));
|
|||
|
|
|||
|
deviceExtension = (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension;
|
|||
|
//
|
|||
|
// Use DpcInterlockMouse to determine whether the DPC is running
|
|||
|
// concurrently on another processor. We only want one instantiation
|
|||
|
// of the DPC to actually do any work. DpcInterlockMouse is -1
|
|||
|
// when no DPC is executing. We increment it, and if the result is
|
|||
|
// zero then the current instantiation is the only one executing, and it
|
|||
|
// is okay to proceed. Otherwise, we just return.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
operationContext.VariableAddress =
|
|||
|
&deviceExtension->DpcInterlockMouse;
|
|||
|
operationContext.Operation = IncrementOperation;
|
|||
|
operationContext.NewValue = &interlockedResult;
|
|||
|
|
|||
|
KeSynchronizeExecution(
|
|||
|
deviceExtension->InterruptObject,
|
|||
|
(PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
|
|||
|
(PVOID) &operationContext
|
|||
|
);
|
|||
|
|
|||
|
moreDpcProcessing = (interlockedResult == 0)? TRUE:FALSE;
|
|||
|
|
|||
|
while (moreDpcProcessing) {
|
|||
|
|
|||
|
dataNotConsumed = 0;
|
|||
|
inputDataConsumed = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Get the port InputData queue pointers synchronously.
|
|||
|
//
|
|||
|
|
|||
|
getPointerContext.DeviceExtension = deviceExtension;
|
|||
|
setPointerContext.DeviceExtension = deviceExtension;
|
|||
|
getPointerContext.DeviceType = (CCHAR) MouseDeviceType;
|
|||
|
setPointerContext.DeviceType = (CCHAR) MouseDeviceType;
|
|||
|
setPointerContext.InputCount = 0;
|
|||
|
|
|||
|
KeSynchronizeExecution(
|
|||
|
deviceExtension->InterruptObject,
|
|||
|
(PKSYNCHRONIZE_ROUTINE) I8xGetDataQueuePointer,
|
|||
|
(PVOID) &getPointerContext
|
|||
|
);
|
|||
|
|
|||
|
if (getPointerContext.InputCount != 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Call the connected class driver's callback ISR with the
|
|||
|
// port InputData queue pointers. If we have to wrap the queue,
|
|||
|
// break the operation into two pieces, and call the class callback
|
|||
|
// ISR once for each piece.
|
|||
|
//
|
|||
|
|
|||
|
classDeviceObject =
|
|||
|
deviceExtension->ConnectData.ClassDeviceObject;
|
|||
|
classService =
|
|||
|
deviceExtension->ConnectData.ClassService;
|
|||
|
ASSERT(classService != NULL);
|
|||
|
|
|||
|
if (getPointerContext.DataOut >= getPointerContext.DataIn) {
|
|||
|
|
|||
|
//
|
|||
|
// We'll have to wrap the InputData circular buffer. Call
|
|||
|
// the class callback ISR with the chunk of data starting at
|
|||
|
// DataOut and ending at the end of the queue.
|
|||
|
//
|
|||
|
|
|||
|
Print(DBG_DPC_NOISE,
|
|||
|
("I8042MouseIsrDpc: calling class callback\n"
|
|||
|
));
|
|||
|
Print(DBG_DPC_INFO,
|
|||
|
("I8042MouseIsrDpc: with Start 0x%x and End 0x%x\n",
|
|||
|
getPointerContext.DataOut,
|
|||
|
deviceExtension->DataEnd
|
|||
|
));
|
|||
|
|
|||
|
(*(PSERVICE_CALLBACK_ROUTINE) classService)(
|
|||
|
classDeviceObject,
|
|||
|
getPointerContext.DataOut,
|
|||
|
deviceExtension->DataEnd,
|
|||
|
&inputDataConsumed
|
|||
|
);
|
|||
|
|
|||
|
dataNotConsumed = ((ULONG)((PUCHAR)
|
|||
|
deviceExtension->DataEnd -
|
|||
|
(PUCHAR) getPointerContext.DataOut)
|
|||
|
/ sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
|
|||
|
|
|||
|
Print(DBG_DPC_INFO,
|
|||
|
("I8042MouseIsrDpc: (Wrap) Call callback consumed %d items, left %d\n",
|
|||
|
inputDataConsumed,
|
|||
|
dataNotConsumed
|
|||
|
));
|
|||
|
|
|||
|
setPointerContext.InputCount += inputDataConsumed;
|
|||
|
|
|||
|
if (dataNotConsumed) {
|
|||
|
setPointerContext.DataOut =
|
|||
|
((PUCHAR)getPointerContext.DataOut) +
|
|||
|
(inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
|
|||
|
} else {
|
|||
|
setPointerContext.DataOut =
|
|||
|
deviceExtension->InputData;
|
|||
|
getPointerContext.DataOut = setPointerContext.DataOut;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the class callback ISR with data remaining in the queue.
|
|||
|
//
|
|||
|
|
|||
|
if ((dataNotConsumed == 0) &&
|
|||
|
(inputDataConsumed < getPointerContext.InputCount)){
|
|||
|
Print(DBG_DPC_NOISE,
|
|||
|
("I8042MouseIsrDpc: calling class callback\n"
|
|||
|
));
|
|||
|
Print(DBG_DPC_INFO,
|
|||
|
("I8042MouseIsrDpc: with Start 0x%x and End 0x%x\n",
|
|||
|
getPointerContext.DataOut,
|
|||
|
getPointerContext.DataIn
|
|||
|
));
|
|||
|
|
|||
|
(*(PSERVICE_CALLBACK_ROUTINE) classService)(
|
|||
|
classDeviceObject,
|
|||
|
getPointerContext.DataOut,
|
|||
|
getPointerContext.DataIn,
|
|||
|
&inputDataConsumed
|
|||
|
);
|
|||
|
|
|||
|
dataNotConsumed = ((ULONG)((PUCHAR) getPointerContext.DataIn -
|
|||
|
(PUCHAR) getPointerContext.DataOut)
|
|||
|
/ sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
|
|||
|
|
|||
|
Print(DBG_DPC_INFO,
|
|||
|
("I8042MouseIsrDpc: Call callback consumed %d items, left %d\n",
|
|||
|
inputDataConsumed,
|
|||
|
dataNotConsumed
|
|||
|
));
|
|||
|
|
|||
|
setPointerContext.DataOut =
|
|||
|
((PUCHAR)getPointerContext.DataOut) +
|
|||
|
(inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
|
|||
|
setPointerContext.InputCount += inputDataConsumed;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Update the port InputData queue DataOut pointer and InputCount
|
|||
|
// synchronously.
|
|||
|
//
|
|||
|
|
|||
|
KeSynchronizeExecution(
|
|||
|
deviceExtension->InterruptObject,
|
|||
|
(PKSYNCHRONIZE_ROUTINE) I8xSetDataQueuePointer,
|
|||
|
(PVOID) &setPointerContext
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (dataNotConsumed) {
|
|||
|
|
|||
|
//
|
|||
|
// The class driver was unable to consume all the data.
|
|||
|
// Reset the interlocked variable to -1. We do not want
|
|||
|
// to attempt to move more data to the class driver at this
|
|||
|
// point, because it is already overloaded. Need to wait a
|
|||
|
// while to give the Raw Input Thread a chance to read some
|
|||
|
// of the data out of the class driver's queue. We accomplish
|
|||
|
// this "wait" via a timer.
|
|||
|
//
|
|||
|
|
|||
|
Print(DBG_DPC_INFO,
|
|||
|
("I8042MouseIsrDpc: set timer in DPC\n"
|
|||
|
));
|
|||
|
|
|||
|
operationContext.Operation = WriteOperation;
|
|||
|
interlockedResult = -1;
|
|||
|
operationContext.NewValue = &interlockedResult;
|
|||
|
|
|||
|
KeSynchronizeExecution(
|
|||
|
deviceExtension->InterruptObject,
|
|||
|
(PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
|
|||
|
(PVOID) &operationContext
|
|||
|
);
|
|||
|
|
|||
|
deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
|
|||
|
deltaTime.HighPart = -1;
|
|||
|
|
|||
|
(VOID) KeSetTimer(
|
|||
|
&deviceExtension->DataConsumptionTimer,
|
|||
|
deltaTime,
|
|||
|
&deviceExtension->MouseIsrDpcRetry
|
|||
|
);
|
|||
|
|
|||
|
moreDpcProcessing = FALSE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Decrement DpcInterlockMouse. If the result goes negative,
|
|||
|
// then we're all finished processing the DPC. Otherwise, either
|
|||
|
// the ISR incremented DpcInterlockMouse because it has more
|
|||
|
// work for the ISR DPC to do, or a concurrent DPC executed on
|
|||
|
// some processor while the current DPC was running (the
|
|||
|
// concurrent DPC wouldn't have done any work). Make sure that
|
|||
|
// the current DPC handles any extra work that is ready to be
|
|||
|
// done.
|
|||
|
//
|
|||
|
|
|||
|
operationContext.Operation = DecrementOperation;
|
|||
|
operationContext.NewValue = &interlockedResult;
|
|||
|
|
|||
|
KeSynchronizeExecution(
|
|||
|
deviceExtension->InterruptObject,
|
|||
|
(PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
|
|||
|
(PVOID) &operationContext
|
|||
|
);
|
|||
|
|
|||
|
if (interlockedResult != -1) {
|
|||
|
|
|||
|
//
|
|||
|
// The interlocked variable is still greater than or equal to
|
|||
|
// zero. Reset it to zero, so that we execute the loop one
|
|||
|
// more time (assuming no more DPCs execute and bump the
|
|||
|
// variable up again).
|
|||
|
//
|
|||
|
|
|||
|
operationContext.Operation = WriteOperation;
|
|||
|
interlockedResult = 0;
|
|||
|
operationContext.NewValue = &interlockedResult;
|
|||
|
|
|||
|
KeSynchronizeExecution(
|
|||
|
deviceExtension->InterruptObject,
|
|||
|
(PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
|
|||
|
(PVOID) &operationContext
|
|||
|
);
|
|||
|
|
|||
|
Print(DBG_DPC_INFO, ("I8042MouseIsrDpc: loop in DPC\n"));
|
|||
|
}
|
|||
|
else {
|
|||
|
moreDpcProcessing = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Print(DBG_DPC_TRACE, ("I8042MouseIsrDpc: exit\n"));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
I8xWriteDataToMouseQueue(
|
|||
|
PPORT_MOUSE_EXTENSION MouseExtension,
|
|||
|
IN PMOUSE_INPUT_DATA InputData
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine adds input data from the mouse to the InputData queue.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MouseExtension - Pointer to the mouse portion of the device extension.
|
|||
|
|
|||
|
InputData - Pointer to the data to add to the InputData queue.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns TRUE if the data was added, otherwise FALSE.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
Print(DBG_CALL_TRACE, ("I8xWriteDataToMouseQueue: enter\n"));
|
|||
|
Print(DBG_CALL_NOISE,
|
|||
|
("I8xWriteDataToMouseQueue: DataIn 0x%x, DataOut 0x%x\n",
|
|||
|
MouseExtension->DataIn,
|
|||
|
MouseExtension->DataOut
|
|||
|
));
|
|||
|
Print(DBG_CALL_NOISE,
|
|||
|
("I8xWriteDataToMouseQueue: InputCount %d\n",
|
|||
|
MouseExtension->InputCount
|
|||
|
));
|
|||
|
|
|||
|
//
|
|||
|
// Check for full input data queue.
|
|||
|
//
|
|||
|
|
|||
|
if ((MouseExtension->DataIn == MouseExtension->DataOut) &&
|
|||
|
(MouseExtension->InputCount != 0)) {
|
|||
|
|
|||
|
//
|
|||
|
// The input data queue is full. Intentionally ignore
|
|||
|
// the new data.
|
|||
|
//
|
|||
|
|
|||
|
Print(DBG_CALL_ERROR, ("I8xWriteDataToMouseQueue: OVERFLOW\n"));
|
|||
|
return(FALSE);
|
|||
|
|
|||
|
} else {
|
|||
|
*(MouseExtension->DataIn) = *InputData;
|
|||
|
MouseExtension->InputCount += 1;
|
|||
|
MouseExtension->DataIn++;
|
|||
|
Print(DBG_DPC_INFO,
|
|||
|
("I8xWriteDataToMouseQueue: new InputCount %d\n",
|
|||
|
MouseExtension->InputCount
|
|||
|
));
|
|||
|
if (MouseExtension->DataIn == MouseExtension->DataEnd) {
|
|||
|
Print(DBG_DPC_NOISE, ("I8xWriteDataToMouseQueue: wrap buffer\n"));
|
|||
|
MouseExtension->DataIn = MouseExtension->InputData;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Print(DBG_DPC_TRACE, ("I8xWriteDataToMouseQueue: exit\n"));
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
}
|