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