|
|
/*++
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); }
|