432 lines
13 KiB
C
432 lines
13 KiB
C
|
||
/*++
|
||
|
||
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);
|
||
}
|
||
|