|
|
/*++
Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved
Module Name:
kbdcmn.c
Abstract:
The common portions of the Intel i8042 port driver which apply to the keyboard 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"
VOID I8042KeyboardIsrDpc( 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 keyboard interrupts. It is queued in the keyboard ISR. The real work is done via a callback to the connected keyboard 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_KEYBOARD_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, ("I8042KeyboardIsrDpc: enter\n"));
deviceExtension = (PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension;
//
// Use DpcInterlockKeyboard to determine whether the DPC is running
// concurrently on another processor. We only want one instantiation
// of the DPC to actually do any work. DpcInterlockKeyboard 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->DpcInterlockKeyboard; 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) KeyboardDeviceType; setPointerContext.DeviceType = (CCHAR) KeyboardDeviceType; 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, ("I8042KeyboardIsrDpc: calling class callback\n" )); Print(DBG_DPC_INFO, ("I8042KeyboardIsrDpc: 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(KEYBOARD_INPUT_DATA)) - inputDataConsumed;
Print(DBG_DPC_INFO, ("I8042KeyboardIsrDpc: (Wrap) Call callback consumed %d items, left %d\n", inputDataConsumed, dataNotConsumed ));
setPointerContext.InputCount += inputDataConsumed;
if (dataNotConsumed) { setPointerContext.DataOut = ((PUCHAR)getPointerContext.DataOut) + (inputDataConsumed * sizeof(KEYBOARD_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, ("I8042KeyboardIsrDpc: calling class callback\n" )); Print(DBG_DPC_INFO, ("I8042KeyboardIsrDpc: 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(KEYBOARD_INPUT_DATA)) - inputDataConsumed;
Print(DBG_DPC_INFO, ("I8042KeyboardIsrDpc: Call callback consumed %d items, left %d\n", inputDataConsumed, dataNotConsumed ));
setPointerContext.DataOut = ((PUCHAR)getPointerContext.DataOut) + (inputDataConsumed * sizeof(KEYBOARD_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, ("I8042KeyboardIsrDpc: 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->KeyboardIsrDpcRetry );
moreDpcProcessing = FALSE;
} else {
//
// Decrement DpcInterlockKeyboard. If the result goes negative,
// then we're all finished processing the DPC. Otherwise, either
// the ISR incremented DpcInterlockKeyboard 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, ("I8042KeyboardIsrDpc: loop in DPC\n" )); } else { moreDpcProcessing = FALSE; } }
}
Print(DBG_DPC_TRACE, ("I8042KeyboardIsrDpc: exit\n")); }
BOOLEAN I8xWriteDataToKeyboardQueue( PPORT_KEYBOARD_EXTENSION KeyboardExtension, IN PKEYBOARD_INPUT_DATA InputData )
/*++
Routine Description:
This routine adds input data from the keyboard to the InputData queue.
Arguments:
KeyboardExtension - Pointer to the keyboard 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.
--*/
{
PKEYBOARD_INPUT_DATA previousDataIn;
Print(DBG_CALL_TRACE, ("I8xWriteDataToKeyboardQueue: enter\n")); Print(DBG_CALL_NOISE, ("I8xWriteDataToKeyboardQueue: DataIn 0x%x, DataOut 0x%x\n", KeyboardExtension->DataIn, KeyboardExtension->DataOut )); Print(DBG_CALL_NOISE, ("I8xWriteDataToKeyboardQueue: InputCount %d\n", KeyboardExtension->InputCount ));
//
// Check for full input data queue.
//
if ((KeyboardExtension->DataIn == KeyboardExtension->DataOut) && (KeyboardExtension->InputCount != 0)) {
//
// Queue overflow. Replace the previous input data packet
// with a keyboard overrun data packet, thus losing both the
// previous and the current input data packet.
//
Print(DBG_CALL_ERROR, ("I8xWriteDataToKeyboardQueue: OVERFLOW\n"));
if (KeyboardExtension->DataIn == KeyboardExtension->InputData) { Print(DBG_CALL_NOISE, ("I8xWriteDataToKeyboardQueue: wrap buffer\n" )); previousDataIn = KeyboardExtension->DataEnd; } else { previousDataIn = KeyboardExtension->DataIn - 1; }
previousDataIn->MakeCode = KEYBOARD_OVERRUN_MAKE_CODE; previousDataIn->Flags = 0;
Print(DBG_CALL_TRACE, ("I8xWriteDataToKeyboardQueue: exit\n")); return(FALSE);
} else { *(KeyboardExtension->DataIn) = *InputData; KeyboardExtension->InputCount += 1; KeyboardExtension->DataIn++; Print(DBG_CALL_INFO, ("I8xWriteDataToKeyboardQueue: new InputCount %d\n", KeyboardExtension->InputCount )); if (KeyboardExtension->DataIn == KeyboardExtension->DataEnd) { Print(DBG_CALL_NOISE, ("I8xWriteDataToKeyboardQueue: wrap buffer\n" )); KeyboardExtension->DataIn = KeyboardExtension->InputData; } }
Print(DBG_CALL_TRACE, ("I8xWriteDataToKeyboardQueue: exit\n"));
return(TRUE); }
|