windows-nt/Source/XPSP1/NT/drivers/input/pnpi8042/moucmn.c

415 lines
13 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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);
}