VBoxPS2NT.cpp revision 7519a1c4323fa86fbb19a36a91cd25abfd7af714
/* $Id$ */
/** @file
* VBox NT4 Mouse Driver
*/
/*
* Copyright (C) 2011-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOG_GROUP LOG_GROUP_DRV_MOUSE
#include <iprt/asm.h>
#include <VBox/err.h>
#include <VBox/log.h>
#include <VBox/VBoxGuestLib.h>
#include <stdarg.h>
#include <string.h>
RT_C_DECLS_BEGIN
#define _InterlockedAddLargeStatistic _InterlockedAddLargeStatistic_StupidDDKVsCompilerCrap
#undef PAGE_SIZE
#undef PAGE_SHIFT
#include <ntddk.h>
#include <ntddkbd.h>
#include <ntddmou.h>
#undef _InterlockedAddLargeStatistic _InterlockedAddLargeStatistic_StupidDDKVsCompilerCrap
RT_C_DECLS_END
/* not available on NT4 */
#undef ExFreePool
#undef ExAllocatePool
/* i8042 mouse status bits */
#define LEFT_BUTTON_DOWN 0x01
#define RIGHT_BUTTON_DOWN 0x02
#define MIDDLE_BUTTON_DOWN 0x04
#define X_DATA_SIGN 0x10
#define Y_DATA_SIGN 0x20
#define X_OVERFLOW 0x40
#define Y_OVERFLOW 0x80
#define MOUSE_SIGN_OVERFLOW_MASK (X_DATA_SIGN | Y_DATA_SIGN | X_OVERFLOW | Y_OVERFLOW)
#define MOUSE_MAXIMUM_POSITIVE_DELTA 0x000000FF
#define MOUSE_MAXIMUM_NEGATIVE_DELTA 0xFFFFFF00
#define KEYBOARD_HARDWARE_PRESENT 0x01
#define MOUSE_HARDWARE_PRESENT 0x02
#define BALLPOINT_HARDWARE_PRESENT 0x04
#define WHEELMOUSE_HARDWARE_PRESENT 0x08
#define I8X_PUT_COMMAND_BYTE(Address, Byte) WRITE_PORT_UCHAR(Address, (UCHAR) Byte)
#define I8X_PUT_DATA_BYTE(Address, Byte) WRITE_PORT_UCHAR(Address, (UCHAR) Byte)
#define I8X_GET_STATUS_BYTE(Address) READ_PORT_UCHAR(Address)
#define I8X_GET_DATA_BYTE(Address) READ_PORT_UCHAR(Address)
/* commands to the i8042 controller */
#define I8042_READ_CONTROLLER_COMMAND_BYTE 0x20
#define I8042_WRITE_CONTROLLER_COMMAND_BYTE 0x60
#define I8042_DISABLE_MOUSE_DEVICE 0xA7
#define I8042_ENABLE_MOUSE_DEVICE 0xA8
#define I8042_AUXILIARY_DEVICE_TEST 0xA9
#define I8042_KEYBOARD_DEVICE_TEST 0xAB
#define I8042_DISABLE_KEYBOARD_DEVICE 0xAD
#define I8042_ENABLE_KEYBOARD_DEVICE 0xAE
#define I8042_WRITE_TO_AUXILIARY_DEVICE 0xD4
/* i8042 Controller Command Byte */
#define CCB_ENABLE_KEYBOARD_INTERRUPT 0x01
#define CCB_ENABLE_MOUSE_INTERRUPT 0x02
#define CCB_DISABLE_KEYBOARD_DEVICE 0x10
#define CCB_DISABLE_MOUSE_DEVICE 0x20
#define CCB_KEYBOARD_TRANSLATE_MODE 0x40
/* i8042 Controller Status Register bits */
#define OUTPUT_BUFFER_FULL 0x01
#define INPUT_BUFFER_FULL 0x02
#define MOUSE_OUTPUT_BUFFER_FULL 0x20
/* i8042 responses */
#define ACKNOWLEDGE 0xFA
#define RESEND 0xFE
/* commands to the keyboard (through the 8042 data port) */
#define SET_KEYBOARD_INDICATORS 0xED
#define SELECT_SCAN_CODE_SET 0xF0
#define READ_KEYBOARD_ID 0xF2
#define SET_KEYBOARD_TYPEMATIC 0xF3
#define SET_ALL_TYPEMATIC_MAKE_BREAK 0xFA
#define KEYBOARD_RESET 0xFF
/* commands to the mouse (through the 8042 data port) */
#define SET_MOUSE_SCALING_1TO1 0xE6
#define SET_MOUSE_RESOLUTION 0xE8
#define READ_MOUSE_STATUS 0xE9
#define GET_DEVICE_ID 0xF2
#define SET_MOUSE_SAMPLING_RATE 0xF3
#define ENABLE_MOUSE_TRANSMISSION 0xF4
#define MOUSE_RESET 0xFF
/* mouse responses */
#define MOUSE_COMPLETE 0xAA
#define MOUSE_ID_BYTE 0x00
#define WHEELMOUSE_ID_BYTE 0x03
/* maximum number of pointer/keyboard port names the port driver */
#define POINTER_PORTS_MAXIMUM 8
#define KEYBOARD_PORTS_MAXIMUM 8
/* NtDeviceIoControlFile internal IoControlCode values for keyboard device */
#define IOCTL_INTERNAL_KEYBOARD_CONNECT CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0080, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_INTERNAL_KEYBOARD_DISCONNECT CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0100, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_INTERNAL_KEYBOARD_ENABLE CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0200, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_INTERNAL_KEYBOARD_DISABLE CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0400, METHOD_NEITHER, FILE_ANY_ACCESS)
/* NtDeviceIoControlFile internal IoControlCode values for mouse device */
#define IOCTL_INTERNAL_MOUSE_CONNECT CTL_CODE(FILE_DEVICE_MOUSE, 0x0080, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_INTERNAL_MOUSE_DISCONNECT CTL_CODE(FILE_DEVICE_MOUSE, 0x0100, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_INTERNAL_MOUSE_ENABLE CTL_CODE(FILE_DEVICE_MOUSE, 0x0200, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_INTERNAL_MOUSE_DISABLE CTL_CODE(FILE_DEVICE_MOUSE, 0x0400, METHOD_NEITHER, FILE_ANY_ACCESS)
/* i8042 controller input/output ports */
typedef enum _I8042IOPORTTYPE
{
i8042Dat = 0,
i8042Cmd,
i8042MaxPorts
} I8042IOPORTTYPE;
/* device types attached to the i8042 controller */
typedef enum _I8042DEVTYPE
{
CtrlDevType,
KbdDevType,
MouDevType,
NoDevice
} I8042DEVTYPE;
/* keyboard output states */
typedef enum _KBDSTATE
{
Idle,
SendFirstByte,
SendLastByte
} KBDSTATE;
/* keyboard scan code input states */
typedef enum _KBDSCANSTATE
{
Normal,
GotE0,
GotE1
} KBDSCANSTATE;
/* mouse states */
typedef enum _MOUSTATE
{
MouseIdle,
XMovement,
YMovement,
ZMovement,
MouseExpectingACK
} MOUSTATE;
typedef VOID (*PSERVICECALLBACK)(PVOID Ctx, PVOID pArg1, PVOID pArg2, PVOID pArg3);
typedef struct _CONNECT_DATA
{
PDEVICE_OBJECT ClassDeviceObject;
PSERVICECALLBACK ClassService;
} CONNECT_DATA, *PCONNECT_DATA;
typedef struct _KBDSETPACKET
{
USHORT State;
UCHAR FirstByte;
UCHAR LastByte;
} KBDSETPACKET, *PKBDSETPACKET;
typedef struct _I8042CFGINF
{
INTERFACE_TYPE InterfaceType; /**< bus interface type */
ULONG uBusNr; /**< bus number */
ULONG cPorts;
CM_PARTIAL_RESOURCE_DESCRIPTOR aPorts[i8042MaxPorts];
CM_PARTIAL_RESOURCE_DESCRIPTOR KbdInt;
CM_PARTIAL_RESOURCE_DESCRIPTOR MouInt;
BOOLEAN fFloatSave; /**< weather to save floating point context */
USHORT iResend; /**< number of retries allowed */
USHORT PollingIterations; /**< number of polling iterations */
USHORT PollingIterationsMaximum;
USHORT PollStatusIterations;
USHORT StallMicroseconds;
KEYBOARD_ATTRIBUTES KbdAttr;
KEYBOARD_TYPEMATIC_PARAMETERS KeyRepeatCurrent;
KEYBOARD_INDICATOR_PARAMETERS KbdInd;
MOUSE_ATTRIBUTES MouAttr;
USHORT MouseResolution;
ULONG EnableWheelDetection;
} I8042CFGINF, *PI8042CFGINF;
typedef struct _PORTKBDEXT
{
CONNECT_DATA ConnectData;
ULONG cInput;
PKEYBOARD_INPUT_DATA InputData;
PKEYBOARD_INPUT_DATA DataIn;
PKEYBOARD_INPUT_DATA DataOut;
PKEYBOARD_INPUT_DATA DataEnd;
KEYBOARD_INPUT_DATA CurrentInput;
KBDSCANSTATE CurrentScanState;
KBDSETPACKET CurrentOutput;
USHORT ResendCount;
KTIMER DataConsumptionTimer;
USHORT UnitId;
} PORTKBDEXT, *PPORTKBDEXT;
typedef struct _PORTMOUEXT
{
CONNECT_DATA ConnectData;
ULONG cInput;
PMOUSE_INPUT_DATA InputData;
PMOUSE_INPUT_DATA DataIn;
PMOUSE_INPUT_DATA DataOut;
PMOUSE_INPUT_DATA DataEnd;
MOUSE_INPUT_DATA CurrentInput;
USHORT InputState;
UCHAR uCurrSignAndOverflow;
UCHAR uPrevSignAndOverflow;
UCHAR PreviousButtons;
KTIMER DataConsumptionTimer;
LARGE_INTEGER PreviousTick;
USHORT UnitId;
ULONG SynchTickCount;
UCHAR LastByteReceived;
} PORTMOUEXT, *PPORTMOUEXT;
typedef struct _DEVEXT
{
ULONG HardwarePresent;
volatile uint32_t KeyboardEnableCount;
volatile uint32_t MouseEnableCount;
PDEVICE_OBJECT pDevObj;
PUCHAR DevRegs[i8042MaxPorts];
PORTKBDEXT KbdExt;
PORTMOUEXT MouExt;
I8042CFGINF Cfg;
PKINTERRUPT KbdIntObj;
PKINTERRUPT MouIntObj;
KSPIN_LOCK ShIntObj;
KDPC RetriesExceededDpc;
KDPC KeyboardIsrDpc;
KDPC KeyboardIsrDpcRetry;
LONG DpcInterlockKeyboard;
KDPC MouseIsrDpc;
KDPC MouseIsrDpcRetry;
LONG DpcInterlockMouse;
KDPC TimeOutDpc;
KTIMER CommandTimer;
LONG TimerCount;
BOOLEAN fUnmapRegs;
VMMDevReqMouseStatus *pReq;
} DEVEXT, *PDEVEXT;
typedef struct _INITEXT
{
DEVEXT DevExt;
} INITEXT, *PINITEXT;
typedef struct _I8042INITDATACTX
{
PDEVEXT pDevExt;
int DevType;
} I8042INITDATACTX, *PI8042INITDATACTX;
typedef struct _I8042TRANSMITCCBCTX
{
ULONG HwDisEnMask;
BOOLEAN fAndOp;
UCHAR ByteMask;
NTSTATUS Status;
} I8042TRANSMITCCBCTX, *PI8042TRANSMITCCBCTX;
typedef struct _GETDATAPTRCTX
{
PDEVEXT pDevExt;
int DevType;
PVOID DataIn;
PVOID DataOut;
ULONG cInput;
} GETDATAPTRCTX, *PGETDATAPTRCTX;
typedef struct _SETDATAPTRCTX
{
PDEVEXT pDevExt;
int DevType;
ULONG cInput;
PVOID DataOut;
} SETDATAPTRCTX, *PSETDATAPTRCTX;
typedef struct _TIMERCTX
{
PDEVICE_OBJECT pDevObj;
PLONG TimerCounter;
LONG NewTimerCount;
} TIMERCTX, *PTIMERCTX;
typedef struct _KBDINITIATECTX
{
PDEVICE_OBJECT pDevObj;
UCHAR FirstByte;
UCHAR LastByte;
} KBDINITIATECTX, *PKBDINITIATECTX;
typedef enum _OPTYPE
{
IncrementOperation,
DecrementOperation,
WriteOperation,
} OPTYPE;
typedef struct _VAROPCTX
{
PLONG VariableAddress;
OPTYPE Operation;
PLONG NewValue;
} VAROPCTX, *PVAROPCTX;
typedef struct _KBDTYPEINFO
{
USHORT cFunctionKeys;
USHORT cIndicators;
USHORT cKeysTotal;
} KBDTYPEINFO;
static const INDICATOR_LIST s_aIndicators[3] =
{
{0x3A, KEYBOARD_CAPS_LOCK_ON},
{0x45, KEYBOARD_NUM_LOCK_ON},
{0x46, KEYBOARD_SCROLL_LOCK_ON}
};
static const KBDTYPEINFO s_aKeybType[4] =
{
{10, 3, 84}, /* PC/XT 83- 84-key keyboard (and compatibles) */
{12, 3, 102}, /* Olivetti M24 102-key keyboard (and compatibles) */
{10, 3, 84}, /* All AT type keyboards (84-86 keys) */
{12, 3, 101} /* Enhanced 101- or 102-key keyboards (and compatibles) */
};
RT_C_DECLS_BEGIN
static NTSTATUS MouFindWheel(PDEVICE_OBJECT pDevObj);
static VOID MouGetRegstry(PINITEXT pInit, PUNICODE_STRING RegistryPath,
PUNICODE_STRING KeyboardDeviceName, PUNICODE_STRING PointerDeviceName);
static NTSTATUS MouInitHw(PDEVICE_OBJECT pDevObj);
static VOID KbdGetRegstry(PINITEXT pInit, PUNICODE_STRING RegistryPath,
PUNICODE_STRING KeyboardDeviceName, PUNICODE_STRING PointerDeviceName);
static NTSTATUS KbdInitHw(PDEVICE_OBJECT pDevObj);
static VOID HwGetRegstry(PINITEXT pInit, PUNICODE_STRING RegistryPath,
PUNICODE_STRING KeyboardDeviceName, PUNICODE_STRING PointerDeviceName);
static VOID CreateResList(PDEVEXT pDevExt, PCM_RESOURCE_LIST *pResList, PULONG pResListSize);
static VOID InitHw(PDEVICE_OBJECT pDevObj);
static NTSTATUS MouCallOut(PVOID pCtx, PUNICODE_STRING PathName,
INTERFACE_TYPE BusType, ULONG uBusNr, PKEY_VALUE_FULL_INFORMATION *pBusInf,
CONFIGURATION_TYPE uCtrlType, ULONG uCtrlNr, PKEY_VALUE_FULL_INFORMATION *pCtrlInf,
CONFIGURATION_TYPE uPrfType, ULONG uPrfNr, PKEY_VALUE_FULL_INFORMATION *pPrfInf);
static NTSTATUS KbdCallOut(PVOID pCtx, PUNICODE_STRING PathName,
INTERFACE_TYPE BusType, ULONG uBusNr, PKEY_VALUE_FULL_INFORMATION *pBusInf,
CONFIGURATION_TYPE uCtrlType, ULONG uCtrlNr, PKEY_VALUE_FULL_INFORMATION *pCtrlInf,
CONFIGURATION_TYPE uPrfType, ULONG uPrfNr, PKEY_VALUE_FULL_INFORMATION *pPrfInf);
/* */ NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING RegistryPath);
RT_C_DECLS_END
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,CreateResList)
#pragma alloc_text(INIT,MouFindWheel)
#pragma alloc_text(INIT,KbdInitHw)
#pragma alloc_text(INIT,KbdGetRegstry)
#pragma alloc_text(INIT,KbdCallOut)
#pragma alloc_text(INIT,MouInitHw)
#pragma alloc_text(INIT,MouGetRegstry)
#pragma alloc_text(INIT,MouCallOut)
#pragma alloc_text(INIT,InitHw)
#pragma alloc_text(INIT,HwGetRegstry)
#pragma alloc_text(INIT,DriverEntry)
#endif
static BOOLEAN MouDataToQueue(PPORTMOUEXT MouExt, PMOUSE_INPUT_DATA InputData)
{
if ( MouExt->DataIn == MouExt->DataOut
&& MouExt->cInput)
return FALSE;
*(MouExt->DataIn) = *InputData;
MouExt->cInput++;
MouExt->DataIn++;
if (MouExt->DataIn == MouExt->DataEnd)
MouExt->DataIn = MouExt->InputData;
return TRUE;
}
static BOOLEAN KbdDataToQueue(PPORTKBDEXT KbdExt, PKEYBOARD_INPUT_DATA InputData)
{
PKEYBOARD_INPUT_DATA previousDataIn;
if ( KbdExt->DataIn == KbdExt->DataOut
&& KbdExt->cInput)
{
if (KbdExt->DataIn == KbdExt->InputData)
previousDataIn = KbdExt->DataEnd;
else
previousDataIn = KbdExt->DataIn - 1;
previousDataIn->MakeCode = KEYBOARD_OVERRUN_MAKE_CODE;
previousDataIn->Flags = 0;
return FALSE;
}
*(KbdExt->DataIn) = *InputData;
KbdExt->cInput++;
KbdExt->DataIn++;
if (KbdExt->DataIn == KbdExt->DataEnd)
KbdExt->DataIn = KbdExt->InputData;
return TRUE;
}
/**
* Queues the current input data to be processed by a DPC outside the ISR
*/
static VOID QueueInput(PDEVICE_OBJECT pDevObj)
{
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
if (pDevExt->MouseEnableCount)
{
pDevExt->MouExt.CurrentInput.UnitId = pDevExt->MouExt.UnitId;
if (!MouDataToQueue(&pDevExt->MouExt, &pDevExt->MouExt.CurrentInput))
{
}
else if (pDevExt->DpcInterlockMouse >= 0)
pDevExt->DpcInterlockMouse++;
else
KeInsertQueueDpc(&pDevExt->MouseIsrDpc, pDevObj->CurrentIrp, NULL);
}
}
/**
* Drain the i8042 controller buffer.
*/
static VOID DrainOutBuf(PUCHAR DataAddress, PUCHAR CommandAddress)
{
UCHAR byte;
for (unsigned i = 0; i < 2000; i++)
{
if (!(I8X_GET_STATUS_BYTE(CommandAddress) & INPUT_BUFFER_FULL))
break;
KeStallExecutionProcessor(500);
}
while (I8X_GET_STATUS_BYTE(CommandAddress) & OUTPUT_BUFFER_FULL)
byte = I8X_GET_DATA_BYTE(DataAddress);
}
/**
* Read a data byte from the controller, keyboard or mouse in polling mode.
*/
static NTSTATUS GetBytePoll(int DevType, PDEVEXT pDevExt, PUCHAR Byte)
{
UCHAR byte;
ULONG i = 0;
UCHAR fMask = (DevType == MouDevType) ? (UCHAR)(OUTPUT_BUFFER_FULL | MOUSE_OUTPUT_BUFFER_FULL)
: (UCHAR) OUTPUT_BUFFER_FULL;
while ( (i < (ULONG)pDevExt->Cfg.PollingIterations)
&& ((UCHAR)((byte = I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd])) & fMask) != fMask))
{
if (byte & OUTPUT_BUFFER_FULL)
*Byte = I8X_GET_DATA_BYTE(pDevExt->DevRegs[i8042Dat]);
else
{
KeStallExecutionProcessor(pDevExt->Cfg.StallMicroseconds);
i++;
}
}
if (i >= (ULONG)pDevExt->Cfg.PollingIterations)
return STATUS_IO_TIMEOUT;
*Byte = I8X_GET_DATA_BYTE(pDevExt->DevRegs[i8042Dat]);
return STATUS_SUCCESS;
}
/**
* Send a command or data byte to the controller, keyboard or mouse.
*/
static NTSTATUS PutBytePoll(CCHAR PortType, BOOLEAN fWaitForAck, int AckDevType, PDEVEXT pDevExt, UCHAR Byte)
{
NTSTATUS status;
if (AckDevType == MouDevType)
{
/* switch to AUX device */
PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_WRITE_TO_AUXILIARY_DEVICE);
}
PUCHAR dataAddress = pDevExt->DevRegs[i8042Dat];
PUCHAR commandAddress = pDevExt->DevRegs[i8042Cmd];
for (unsigned j = 0; j < (unsigned)pDevExt->Cfg.iResend; j++)
{
unsigned i = 0;
while ( i++ < (ULONG)pDevExt->Cfg.PollingIterations
&& (I8X_GET_STATUS_BYTE(commandAddress) & INPUT_BUFFER_FULL))
KeStallExecutionProcessor(pDevExt->Cfg.StallMicroseconds);
if (i >= (ULONG)pDevExt->Cfg.PollingIterations)
return STATUS_IO_TIMEOUT;
DrainOutBuf(dataAddress, commandAddress);
if (PortType == i8042Cmd)
I8X_PUT_COMMAND_BYTE(commandAddress, Byte);
else
I8X_PUT_DATA_BYTE(dataAddress, Byte);
if (!fWaitForAck)
return STATUS_SUCCESS;
BOOLEAN fKeepTrying = FALSE;
UCHAR byte;
while ((status = GetBytePoll(AckDevType, pDevExt, &byte)) == STATUS_SUCCESS)
{
if (byte == ACKNOWLEDGE)
break;
else if (byte == RESEND)
{
if (AckDevType == MouDevType)
PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_WRITE_TO_AUXILIARY_DEVICE);
fKeepTrying = TRUE;
break;
}
}
if (!fKeepTrying)
return status;
}
return STATUS_IO_TIMEOUT;
}
/**
* Read a byte from controller, keyboard or mouse
*/
static VOID GetByteAsync(int DevType, PDEVEXT pDevExt, PUCHAR pByte)
{
UCHAR byte;
UCHAR fMask;
ULONG i = 0;
fMask = (DevType == MouDevType)
? (UCHAR) (OUTPUT_BUFFER_FULL | MOUSE_OUTPUT_BUFFER_FULL)
: (UCHAR) OUTPUT_BUFFER_FULL;
while ( i < (ULONG)pDevExt->Cfg.PollingIterations
&& ((UCHAR)((byte = I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd])) & fMask) != fMask))
{
if (byte & OUTPUT_BUFFER_FULL)
*pByte = I8X_GET_DATA_BYTE(pDevExt->DevRegs[i8042Dat]);
else
i++;
}
if (i >= (ULONG)pDevExt->Cfg.PollingIterations)
return;
*pByte = I8X_GET_DATA_BYTE(pDevExt->DevRegs[i8042Dat]);
}
/**
* Send a command or data byte to the controller, keyboard or mouse
* asynchronously.
*/
static VOID PutByteAsync(CCHAR PortType, PDEVEXT pDevExt, UCHAR Byte)
{
unsigned i = 0;
while (I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd]) & INPUT_BUFFER_FULL)
if (i++ >= (ULONG)pDevExt->Cfg.PollingIterations)
return;
if (PortType == i8042Cmd)
I8X_PUT_COMMAND_BYTE(pDevExt->DevRegs[i8042Cmd], Byte);
else
I8X_PUT_DATA_BYTE(pDevExt->DevRegs[i8042Dat], Byte);
}
/**
* Initiaze an I/O operation for the keyboard device.
*/
static VOID KbdStartIO(PVOID pCtx)
{
PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pCtx;
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
pDevExt->TimerCount = 3;
KBDSETPACKET keyboardPacket = pDevExt->KbdExt.CurrentOutput;
if (pDevExt->KbdExt.CurrentOutput.State == SendFirstByte)
PutByteAsync(i8042Dat, pDevExt, keyboardPacket.FirstByte);
else if (pDevExt->KbdExt.CurrentOutput.State == SendLastByte)
PutByteAsync(i8042Dat, pDevExt, keyboardPacket.LastByte);
else
ASSERT(FALSE);
}
static BOOLEAN KbdStartWrapper(PVOID pCtx)
{
PDEVICE_OBJECT pDevObj = ((PKBDINITIATECTX)pCtx)->pDevObj;
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
pDevExt->KbdExt.CurrentOutput.State = SendFirstByte;
pDevExt->KbdExt.CurrentOutput.FirstByte = ((PKBDINITIATECTX)pCtx)->FirstByte;
pDevExt->KbdExt.CurrentOutput.LastByte = ((PKBDINITIATECTX)pCtx)->LastByte;
pDevExt->KbdExt.ResendCount = 0;
KbdStartIO(pDevObj);
return TRUE;
}
static BOOLEAN DecTimer(PVOID pCtx)
{
PTIMERCTX pTmCtx = (PTIMERCTX)pCtx;
PDEVICE_OBJECT pDevObj = pTmCtx->pDevObj;
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
if (*(pTmCtx->TimerCounter) != -1)
(*(pTmCtx->TimerCounter))--;
pTmCtx->NewTimerCount = *(pTmCtx->TimerCounter);
if (*(pTmCtx->TimerCounter) == 0)
{
pDevExt->KbdExt.CurrentOutput.State = Idle;
pDevExt->KbdExt.ResendCount = 0;
}
return TRUE;
}
/**
* Perform an operation on the InterlockedDpcVariable.
*/
static BOOLEAN DpcVarOp(PVOID pCtx)
{
PVAROPCTX pOpCtx = (PVAROPCTX)pCtx;
switch (pOpCtx->Operation)
{
case IncrementOperation:
(*pOpCtx->VariableAddress)++;
break;
case DecrementOperation:
(*pOpCtx->VariableAddress)--;
break;
case WriteOperation:
*pOpCtx->VariableAddress = *pOpCtx->NewValue;
break;
default:
ASSERT(FALSE);
break;
}
*(pOpCtx->NewValue) = *(pOpCtx->VariableAddress);
return TRUE;
}
static BOOLEAN GetDataQueuePtr(PVOID pCtx)
{
PDEVEXT pDevExt = (PDEVEXT)((PGETDATAPTRCTX)pCtx)->pDevExt;
CCHAR DevType = (CCHAR)((PGETDATAPTRCTX)pCtx)->DevType;
if (DevType == KbdDevType)
{
((PGETDATAPTRCTX)pCtx)->DataIn = pDevExt->KbdExt.DataIn;
((PGETDATAPTRCTX)pCtx)->DataOut = pDevExt->KbdExt.DataOut;
((PGETDATAPTRCTX)pCtx)->cInput = pDevExt->KbdExt.cInput;
}
else if (DevType == MouDevType)
{
((PGETDATAPTRCTX)pCtx)->DataIn = pDevExt->MouExt.DataIn;
((PGETDATAPTRCTX)pCtx)->DataOut = pDevExt->MouExt.DataOut;
((PGETDATAPTRCTX)pCtx)->cInput = pDevExt->MouExt.cInput;
}
else
ASSERT(FALSE);
return TRUE;
}
static BOOLEAN InitDataQueue(PVOID pCtx)
{
PDEVEXT pDevExt = (PDEVEXT)((PI8042INITDATACTX)pCtx)->pDevExt;
CCHAR DevType = (CCHAR) ((PI8042INITDATACTX)pCtx)->DevType;
if (DevType == KbdDevType)
{
pDevExt->KbdExt.cInput = 0;
pDevExt->KbdExt.DataIn = pDevExt->KbdExt.InputData;
pDevExt->KbdExt.DataOut = pDevExt->KbdExt.InputData;
}
else if (DevType == MouDevType)
{
pDevExt->MouExt.cInput = 0;
pDevExt->MouExt.DataIn = pDevExt->MouExt.InputData;
pDevExt->MouExt.DataOut = pDevExt->MouExt.InputData;
}
else
ASSERT(FALSE);
return TRUE;
}
static BOOLEAN SetDataQueuePtr(PVOID pCtx)
{
PDEVEXT pDevExt = (PDEVEXT)((PSETDATAPTRCTX)pCtx)->pDevExt;
CCHAR DevType = (CCHAR) ((PSETDATAPTRCTX)pCtx)->DevType;
if (DevType == KbdDevType)
{
pDevExt->KbdExt.DataOut = (PKEYBOARD_INPUT_DATA)((PSETDATAPTRCTX)pCtx)->DataOut;
pDevExt->KbdExt.cInput -= ((PSETDATAPTRCTX)pCtx)->cInput;
}
else if (DevType == MouDevType)
{
pDevExt->MouExt.DataOut = (PMOUSE_INPUT_DATA)((PSETDATAPTRCTX)pCtx)->DataOut;
pDevExt->MouExt.cInput -= ((PSETDATAPTRCTX)pCtx)->cInput;
}
else
ASSERT(FALSE);
return TRUE;
}
/**
* DISPATCH_LEVEL IRQL: Complete requests.
*/
static VOID CompleteDpc(PKDPC Dpc, PDEVICE_OBJECT pDevObj, PIRP Irp, PVOID pCtx)
{
NOREF(Dpc);
NOREF(pCtx);
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
KeCancelTimer(&pDevExt->CommandTimer);
Irp = pDevObj->CurrentIrp;
ASSERT(Irp);
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
switch (irpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_KEYBOARD_SET_INDICATORS:
pDevExt->Cfg.KbdInd = *(PKEYBOARD_INDICATOR_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
break;
case IOCTL_KEYBOARD_SET_TYPEMATIC:
pDevExt->Cfg.KeyRepeatCurrent = *(PKEYBOARD_TYPEMATIC_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
break;
default:
break;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
IoStartNextPacket(pDevObj, FALSE);
IoCompleteRequest (Irp, IO_KEYBOARD_INCREMENT);
}
static NTSTATUS I8042Flush(PDEVICE_OBJECT pDevObj, PIRP Irp)
{
NOREF(pDevObj);
NOREF(Irp);
return STATUS_NOT_IMPLEMENTED;
}
/**
* Dispatch internal device control requests.
*/
static NTSTATUS I8042DevCtrl(PDEVICE_OBJECT pDevObj, PIRP Irp)
{
NTSTATUS status;
I8042INITDATACTX initDataCtx;
PVOID pParams;
PKEYBOARD_ATTRIBUTES KbdAttr;
ULONG cbTrans;
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
Irp->IoStatus.Information = 0;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
switch (irpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_INTERNAL_KEYBOARD_CONNECT:
if ((pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT) != KEYBOARD_HARDWARE_PRESENT)
{
status = STATUS_NO_SUCH_DEVICE;
break;
}
else if (pDevExt->KbdExt.ConnectData.ClassService)
{
status = STATUS_SHARING_VIOLATION;
break;
}
else if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA))
{
status = STATUS_INVALID_PARAMETER;
break;
}
pDevExt->KbdExt.ConnectData = *((PCONNECT_DATA) (irpSp->Parameters.DeviceIoControl.Type3InputBuffer));
initDataCtx.pDevExt = pDevExt;
initDataCtx.DevType = KbdDevType;
KeSynchronizeExecution(pDevExt->KbdIntObj, InitDataQueue, &initDataCtx);
status = STATUS_SUCCESS;
break;
case IOCTL_INTERNAL_MOUSE_CONNECT:
if ((pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT) != MOUSE_HARDWARE_PRESENT)
{
status = STATUS_NO_SUCH_DEVICE;
break;
}
else if (pDevExt->MouExt.ConnectData.ClassService)
{
status = STATUS_SHARING_VIOLATION;
break;
}
else if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA))
{
status = STATUS_INVALID_PARAMETER;
break;
}
pDevExt->MouExt.ConnectData = *((PCONNECT_DATA) (irpSp->Parameters.DeviceIoControl.Type3InputBuffer));
initDataCtx.pDevExt = pDevExt;
initDataCtx.DevType = MouDevType;
KeSynchronizeExecution(pDevExt->MouIntObj, InitDataQueue, &initDataCtx);
status = STATUS_SUCCESS;
break;
case IOCTL_INTERNAL_KEYBOARD_DISCONNECT:
case IOCTL_INTERNAL_MOUSE_DISCONNECT:
status = STATUS_NOT_IMPLEMENTED;
break;
case IOCTL_INTERNAL_KEYBOARD_ENABLE:
case IOCTL_INTERNAL_KEYBOARD_DISABLE:
case IOCTL_INTERNAL_MOUSE_ENABLE:
case IOCTL_INTERNAL_MOUSE_DISABLE:
status = STATUS_PENDING;
break;
case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_ATTRIBUTES))
status = STATUS_BUFFER_TOO_SMALL;
else
{
*(PKEYBOARD_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer = pDevExt->Cfg.KbdAttr;
Irp->IoStatus.Information = sizeof(KEYBOARD_ATTRIBUTES);
status = STATUS_SUCCESS;
}
break;
case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
cbTrans = sizeof(KEYBOARD_INDICATOR_TRANSLATION)
+ (sizeof(INDICATOR_LIST) * (pDevExt->Cfg.KbdAttr.NumberOfIndicators - 1));
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < cbTrans)
status = STATUS_BUFFER_TOO_SMALL;
else
{
((PKEYBOARD_INDICATOR_TRANSLATION)
Irp->AssociatedIrp.SystemBuffer)->NumberOfIndicatorKeys = pDevExt->Cfg.KbdAttr.NumberOfIndicators;
RtlMoveMemory(((PKEYBOARD_INDICATOR_TRANSLATION)
Irp->AssociatedIrp.SystemBuffer)->IndicatorList, (PCHAR)s_aIndicators, cbTrans);
Irp->IoStatus.Information = cbTrans;
status = STATUS_SUCCESS;
}
break;
case IOCTL_KEYBOARD_QUERY_INDICATORS:
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS))
status = STATUS_BUFFER_TOO_SMALL;
else
{
*(PKEYBOARD_INDICATOR_PARAMETERS)Irp->AssociatedIrp.SystemBuffer = pDevExt->Cfg.KbdInd;
Irp->IoStatus.Information = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
status = STATUS_SUCCESS;
}
break;
case IOCTL_KEYBOARD_SET_INDICATORS:
if ( (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS))
|| ( (((PKEYBOARD_INDICATOR_PARAMETERS)Irp->AssociatedIrp.SystemBuffer)->LedFlags
& ~(KEYBOARD_SCROLL_LOCK_ON | KEYBOARD_NUM_LOCK_ON | KEYBOARD_CAPS_LOCK_ON)) != 0))
status = STATUS_INVALID_PARAMETER;
else
status = STATUS_PENDING;
break;
case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_TYPEMATIC_PARAMETERS))
status = STATUS_BUFFER_TOO_SMALL;
else
{
*(PKEYBOARD_TYPEMATIC_PARAMETERS)Irp->AssociatedIrp.SystemBuffer = pDevExt->Cfg.KeyRepeatCurrent;
Irp->IoStatus.Information = sizeof(KEYBOARD_TYPEMATIC_PARAMETERS);
status = STATUS_SUCCESS;
}
break;
case IOCTL_KEYBOARD_SET_TYPEMATIC:
pParams = Irp->AssociatedIrp.SystemBuffer;
KbdAttr = &pDevExt->Cfg.KbdAttr;
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)
|| ((PKEYBOARD_TYPEMATIC_PARAMETERS)pParams)->Rate < KbdAttr->KeyRepeatMinimum.Rate
|| ((PKEYBOARD_TYPEMATIC_PARAMETERS)pParams)->Rate > KbdAttr->KeyRepeatMaximum.Rate
|| ((PKEYBOARD_TYPEMATIC_PARAMETERS)pParams)->Delay < KbdAttr->KeyRepeatMinimum.Delay
|| ((PKEYBOARD_TYPEMATIC_PARAMETERS)pParams)->Delay > KbdAttr->KeyRepeatMaximum.Delay)
status = STATUS_INVALID_PARAMETER;
else
status = STATUS_PENDING;
break;
case IOCTL_MOUSE_QUERY_ATTRIBUTES:
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES))
status = STATUS_BUFFER_TOO_SMALL;
else
{
*(PMOUSE_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer = pDevExt->Cfg.MouAttr;
Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
status = STATUS_SUCCESS;
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
Irp->IoStatus.Status = status;
if (status == STATUS_PENDING)
{
IoMarkIrpPending(Irp);
IoStartPacket(pDevObj, Irp, (PULONG)NULL, NULL);
}
else
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
/**
* Dispatch routine for create/open and close requests.
*/
static NTSTATUS I8042OpenClose(PDEVICE_OBJECT pDevObj, PIRP Irp)
{
NOREF(pDevObj);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
/**
* DISPATCH_LEVEL IRQL: Complete requests that have exceeded the maximum
* number of retries.
*/
static VOID CtrlRetriesExceededDpc(PKDPC Dpc, PDEVICE_OBJECT pDevObj, PIRP Irp, PVOID pCtx)
{
NOREF(Dpc);
NOREF(pCtx);
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
Irp->IoStatus.Status = STATUS_IO_TIMEOUT;
IoStartNextPacket(pDevObj, FALSE);
IoCompleteRequest (Irp, IO_KEYBOARD_INCREMENT);
}
static UCHAR TypematicPeriod[] =
{
31, 31, 28, 26, 23, 20, 18, 17, 15, 13, 12, 11, 10, 9,
9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 1, 1, 1
};
/**
* Convert typematic rate/delay to a value expected by the keyboard:
* - bit 7 is zero
* - bits 5...6 indicate the delay
* - bits 0...4 indicate the rate
*/
static UCHAR ConvertTypematic(USHORT Rate, USHORT Delay)
{
UCHAR value = (UCHAR) ((Delay / 250) - 1);
value <<= 5;
if (Rate <= 27)
value |= TypematicPeriod[Rate];
return value;
}
/**
* Start an I/O operation for the device.
*/
static VOID I8042StartIo(PDEVICE_OBJECT pDevObj, PIRP Irp)
{
KBDINITIATECTX keyboardInitiateContext;
LARGE_INTEGER deltaTime;
LONG interlockedResult;
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
switch (irpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_INTERNAL_KEYBOARD_ENABLE:
interlockedResult = ASMAtomicIncU32(&pDevExt->KeyboardEnableCount);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoStartNextPacket(pDevObj, FALSE);
IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
break;
case IOCTL_INTERNAL_KEYBOARD_DISABLE:
if (pDevExt->KeyboardEnableCount == 0)
Irp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR;
else
{
ASMAtomicDecU32(&pDevExt->KeyboardEnableCount);
Irp->IoStatus.Status = STATUS_SUCCESS;
}
IoStartNextPacket(pDevObj, FALSE);
IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
break;
case IOCTL_INTERNAL_MOUSE_ENABLE:
ASMAtomicIncU32(&pDevExt->MouseEnableCount);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoStartNextPacket(pDevObj, FALSE);
IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
break;
case IOCTL_INTERNAL_MOUSE_DISABLE:
if (pDevExt->MouseEnableCount == 0)
Irp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR;
else
{
ASMAtomicDecU32(&pDevExt->MouseEnableCount);
Irp->IoStatus.Status = STATUS_SUCCESS;
}
IoStartNextPacket(pDevObj, FALSE);
IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
break;
case IOCTL_KEYBOARD_SET_INDICATORS:
keyboardInitiateContext.pDevObj = pDevObj;
keyboardInitiateContext.FirstByte = SET_KEYBOARD_INDICATORS;
keyboardInitiateContext.LastByte =
(UCHAR) ((PKEYBOARD_INDICATOR_PARAMETERS)Irp->AssociatedIrp.SystemBuffer)->LedFlags;
KeSynchronizeExecution(pDevExt->KbdIntObj, KbdStartWrapper, &keyboardInitiateContext);
deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
deltaTime.HighPart = -1;
KeSetTimer(&pDevExt->CommandTimer, deltaTime, &pDevExt->TimeOutDpc);
break;
case IOCTL_KEYBOARD_SET_TYPEMATIC:
keyboardInitiateContext.pDevObj = pDevObj;
keyboardInitiateContext.FirstByte = SET_KEYBOARD_TYPEMATIC;
keyboardInitiateContext.LastByte =
ConvertTypematic(((PKEYBOARD_TYPEMATIC_PARAMETERS)Irp->AssociatedIrp.SystemBuffer)->Rate,
((PKEYBOARD_TYPEMATIC_PARAMETERS)Irp->AssociatedIrp.SystemBuffer)->Delay);
KeSynchronizeExecution(pDevExt->KbdIntObj, KbdStartWrapper, &keyboardInitiateContext);
deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
deltaTime.HighPart = -1;
KeSetTimer(&pDevExt->CommandTimer, deltaTime, &pDevExt->TimeOutDpc);
break;
default:
ASSERT(FALSE);
break;
}
}
/**
* Driver's command timeout routine.
*/
static VOID CtrlTimeoutDpc(PKDPC Dpc, PDEVICE_OBJECT pDevObj, PVOID SystemContext1, PVOID SystemContext2)
{
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
KIRQL cancelIrql;
IoAcquireCancelSpinLock(&cancelIrql);
PIRP irp = pDevObj->CurrentIrp;
if (!irp)
{
IoReleaseCancelSpinLock(cancelIrql);
return;
}
IoSetCancelRoutine(irp, NULL);
IoReleaseCancelSpinLock(cancelIrql);
TIMERCTX timerContext;
timerContext.pDevObj = pDevObj;
timerContext.TimerCounter = &pDevExt->TimerCount;
KeSynchronizeExecution(pDevExt->KbdIntObj, DecTimer, &timerContext);
if (timerContext.NewTimerCount == 0)
{
pDevObj->CurrentIrp->IoStatus.Information = 0;
pDevObj->CurrentIrp->IoStatus.Status = STATUS_IO_TIMEOUT;
IoStartNextPacket(pDevObj, FALSE);
IoCompleteRequest(irp, IO_KEYBOARD_INCREMENT);
}
else
{
LARGE_INTEGER deltaTime;
deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
deltaTime.HighPart = -1;
KeSetTimer(&pDevExt->CommandTimer, deltaTime, &pDevExt->TimeOutDpc);
}
}
/**
* DISPATCH_LEVEL IRQL: Finish processing for keyboard interrupts.
*/
static VOID CtrlKbdIsrDpc(PKDPC Dpc, PDEVICE_OBJECT pDevObj, PIRP Irp, PVOID pCtx)
{
NOREF(Dpc);
NOREF(Irp);
NOREF(pCtx);
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
VAROPCTX opCtx;
LONG interlockedResult;
opCtx.VariableAddress = &pDevExt->DpcInterlockKeyboard;
opCtx.Operation = IncrementOperation;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->KbdIntObj, DpcVarOp, (PVOID)&opCtx);
BOOLEAN fContinue = (interlockedResult == 0) ? TRUE : FALSE;
while (fContinue)
{
ULONG cbNotConsumed = 0;
ULONG inputDataConsumed = 0;
GETDATAPTRCTX getPtrCtx;
getPtrCtx.pDevExt = pDevExt;
getPtrCtx.DevType = KbdDevType;
SETDATAPTRCTX setPtrCtx;
setPtrCtx.pDevExt = pDevExt;
setPtrCtx.DevType = KbdDevType;
setPtrCtx.cInput = 0;
KeSynchronizeExecution(pDevExt->KbdIntObj, GetDataQueuePtr, &getPtrCtx);
if (getPtrCtx.cInput)
{
PVOID classDeviceObject = pDevExt->KbdExt.ConnectData.ClassDeviceObject;
PSERVICECALLBACK classService = pDevExt->KbdExt.ConnectData.ClassService;
ASSERT(classService);
if (getPtrCtx.DataOut >= getPtrCtx.DataIn)
{
classService(classDeviceObject, getPtrCtx.DataOut, pDevExt->KbdExt.DataEnd, &inputDataConsumed);
cbNotConsumed = (((PUCHAR) pDevExt->KbdExt.DataEnd - (PUCHAR) getPtrCtx.DataOut)
/ sizeof(KEYBOARD_INPUT_DATA)) - inputDataConsumed;
setPtrCtx.cInput += inputDataConsumed;
if (cbNotConsumed)
{
setPtrCtx.DataOut = ((PUCHAR)getPtrCtx.DataOut)
+ (inputDataConsumed * sizeof(KEYBOARD_INPUT_DATA));
}
else
{
setPtrCtx.DataOut = pDevExt->KbdExt.InputData;
getPtrCtx.DataOut = setPtrCtx.DataOut;
}
}
if ( cbNotConsumed == 0
&& inputDataConsumed < getPtrCtx.cInput)
{
classService(classDeviceObject, getPtrCtx.DataOut, getPtrCtx.DataIn, &inputDataConsumed);
cbNotConsumed = (((PUCHAR) getPtrCtx.DataIn - (PUCHAR) getPtrCtx.DataOut)
/ sizeof(KEYBOARD_INPUT_DATA)) - inputDataConsumed;
setPtrCtx.DataOut = ((PUCHAR)getPtrCtx.DataOut) +
(inputDataConsumed * sizeof(KEYBOARD_INPUT_DATA));
setPtrCtx.cInput += inputDataConsumed;
}
KeSynchronizeExecution(pDevExt->KbdIntObj, SetDataQueuePtr, &setPtrCtx);
}
if (cbNotConsumed)
{
opCtx.Operation = WriteOperation;
interlockedResult = -1;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->KbdIntObj, DpcVarOp, &opCtx);
LARGE_INTEGER deltaTime;
deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
deltaTime.HighPart = -1;
KeSetTimer(&pDevExt->KbdExt.DataConsumptionTimer, deltaTime, &pDevExt->KeyboardIsrDpcRetry);
fContinue = FALSE;
}
else
{
opCtx.Operation = DecrementOperation;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->KbdIntObj, DpcVarOp, &opCtx);
if (interlockedResult != -1)
{
opCtx.Operation = WriteOperation;
interlockedResult = 0;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->KbdIntObj, DpcVarOp, &opCtx);
}
else
fContinue = FALSE;
}
}
}
/**
* DISPATCH_LEVEL IRQL: Finish processing of mouse interrupts
*/
static VOID CtrlMouIsrDpc(PKDPC Dpc, PDEVICE_OBJECT pDevObj, PIRP Irp, PVOID pCtx)
{
NOREF(Dpc);
NOREF(Irp);
NOREF(pCtx);
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
VAROPCTX opCtx;
LONG interlockedResult;
opCtx.VariableAddress = &pDevExt->DpcInterlockMouse;
opCtx.Operation = IncrementOperation;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->MouIntObj, DpcVarOp, &opCtx);
BOOLEAN fContinue = (interlockedResult == 0) ? TRUE : FALSE;
while (fContinue)
{
ULONG cbNotConsumed = 0;
ULONG inputDataConsumed = 0;
GETDATAPTRCTX getPtrCtx;
getPtrCtx.pDevExt = pDevExt;
getPtrCtx.DevType = MouDevType;
SETDATAPTRCTX setPtrCtx;
setPtrCtx.pDevExt = pDevExt;
setPtrCtx.DevType = MouDevType;
setPtrCtx.cInput = 0;
KeSynchronizeExecution(pDevExt->MouIntObj, GetDataQueuePtr, &getPtrCtx);
if (getPtrCtx.cInput)
{
PVOID classDeviceObject = pDevExt->MouExt.ConnectData.ClassDeviceObject;
PSERVICECALLBACK classService = pDevExt->MouExt.ConnectData.ClassService;
ASSERT(classService);
if (getPtrCtx.DataOut >= getPtrCtx.DataIn)
{
classService(classDeviceObject, getPtrCtx.DataOut, pDevExt->MouExt.DataEnd, &inputDataConsumed);
cbNotConsumed = (((PUCHAR)pDevExt->MouExt.DataEnd - (PUCHAR) getPtrCtx.DataOut)
/ sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
setPtrCtx.cInput += inputDataConsumed;
if (cbNotConsumed)
{
setPtrCtx.DataOut = ((PUCHAR)getPtrCtx.DataOut) + (inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
}
else
{
setPtrCtx.DataOut = pDevExt->MouExt.InputData;
getPtrCtx.DataOut = setPtrCtx.DataOut;
}
}
if ( cbNotConsumed == 0
&& inputDataConsumed < getPtrCtx.cInput)
{
classService(classDeviceObject, getPtrCtx.DataOut, getPtrCtx.DataIn, &inputDataConsumed);
cbNotConsumed = (((PUCHAR) getPtrCtx.DataIn - (PUCHAR) getPtrCtx.DataOut)
/ sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
setPtrCtx.DataOut = ((PUCHAR)getPtrCtx.DataOut) + (inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
setPtrCtx.cInput += inputDataConsumed;
}
KeSynchronizeExecution(pDevExt->MouIntObj, SetDataQueuePtr, &setPtrCtx);
}
if (cbNotConsumed)
{
opCtx.Operation = WriteOperation;
interlockedResult = -1;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->MouIntObj, DpcVarOp, &opCtx);
LARGE_INTEGER deltaTime;
deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
deltaTime.HighPart = -1;
KeSetTimer(&pDevExt->MouExt.DataConsumptionTimer, deltaTime, &pDevExt->MouseIsrDpcRetry);
fContinue = FALSE;
}
else
{
opCtx.Operation = DecrementOperation;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->MouIntObj, DpcVarOp, &opCtx);
if (interlockedResult != -1)
{
opCtx.Operation = WriteOperation;
interlockedResult = 0;
opCtx.NewValue = &interlockedResult;
KeSynchronizeExecution(pDevExt->MouIntObj, DpcVarOp, &opCtx);
}
else
fContinue = FALSE;
}
}
}
/**
* Interrupt service routine for the mouse device.
*/
static BOOLEAN MouIntHandler(PKINTERRUPT Interrupt, PVOID pCtx)
{
UCHAR uPrevSignAndOverflow;
NOREF(Interrupt);
PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pCtx;
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
if ((I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd]) & (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
!= (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
{
KeStallExecutionProcessor(10);
if ((I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd]) & (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
!= (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
return FALSE;
}
UCHAR byte;
GetByteAsync(MouDevType, pDevExt, &byte);
if ( pDevExt->MouExt.LastByteReceived == 0xaa
&& byte == 0x00)
{
pDevExt->HardwarePresent &= ~WHEELMOUSE_HARDWARE_PRESENT;
pDevExt->Cfg.MouAttr.NumberOfButtons = 2;
PutByteAsync(i8042Cmd, pDevExt, I8042_WRITE_TO_AUXILIARY_DEVICE);
PutByteAsync(i8042Dat, pDevExt, ENABLE_MOUSE_TRANSMISSION);
pDevExt->MouExt.InputState = MouseExpectingACK;
}
pDevExt->MouExt.LastByteReceived = byte;
LARGE_INTEGER tickDelta, newTick;
KeQueryTickCount(&newTick);
tickDelta.QuadPart = newTick.QuadPart - pDevExt->MouExt.PreviousTick.QuadPart;
if ( pDevExt->MouExt.InputState != MouseIdle
&& pDevExt->MouExt.InputState != MouseExpectingACK
&& (tickDelta.LowPart >= pDevExt->MouExt.SynchTickCount || tickDelta.HighPart != 0))
pDevExt->MouExt.InputState = MouseIdle;
pDevExt->MouExt.PreviousTick = newTick;
switch (pDevExt->MouExt.InputState)
{
case MouseIdle:
{
UCHAR fPrevBtns = pDevExt->MouExt.PreviousButtons;
pDevExt->MouExt.CurrentInput.ButtonFlags = 0;
pDevExt->MouExt.CurrentInput.ButtonData = 0;
if ((!(fPrevBtns & LEFT_BUTTON_DOWN)) && (byte & LEFT_BUTTON_DOWN))
pDevExt->MouExt.CurrentInput.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
else if ((fPrevBtns & LEFT_BUTTON_DOWN) && !(byte & LEFT_BUTTON_DOWN))
pDevExt->MouExt.CurrentInput.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
if ((!(fPrevBtns & RIGHT_BUTTON_DOWN)) && (byte & RIGHT_BUTTON_DOWN))
pDevExt->MouExt.CurrentInput.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
else if ((fPrevBtns & RIGHT_BUTTON_DOWN) && !(byte & RIGHT_BUTTON_DOWN))
pDevExt->MouExt.CurrentInput.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
if ((!(fPrevBtns & MIDDLE_BUTTON_DOWN)) && (byte & MIDDLE_BUTTON_DOWN))
pDevExt->MouExt.CurrentInput.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
else if ((fPrevBtns & MIDDLE_BUTTON_DOWN) && !(byte & MIDDLE_BUTTON_DOWN))
pDevExt->MouExt.CurrentInput.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
pDevExt->MouExt.PreviousButtons = byte & (RIGHT_BUTTON_DOWN|MIDDLE_BUTTON_DOWN|LEFT_BUTTON_DOWN);
pDevExt->MouExt.uCurrSignAndOverflow = (UCHAR) (byte & MOUSE_SIGN_OVERFLOW_MASK);
pDevExt->MouExt.InputState = XMovement;
break;
}
case XMovement:
{
if (pDevExt->MouExt.uCurrSignAndOverflow & X_OVERFLOW)
{
uPrevSignAndOverflow = pDevExt->MouExt.uPrevSignAndOverflow;
if (uPrevSignAndOverflow & X_OVERFLOW)
{
if ((uPrevSignAndOverflow & X_DATA_SIGN) != (pDevExt->MouExt.uCurrSignAndOverflow & X_DATA_SIGN))
pDevExt->MouExt.uCurrSignAndOverflow ^= X_DATA_SIGN;
}
if (pDevExt->MouExt.uCurrSignAndOverflow & X_DATA_SIGN)
pDevExt->MouExt.CurrentInput.LastX = MOUSE_MAXIMUM_NEGATIVE_DELTA;
else
pDevExt->MouExt.CurrentInput.LastX = MOUSE_MAXIMUM_POSITIVE_DELTA;
}
else
{
pDevExt->MouExt.CurrentInput.LastX = (ULONG) byte;
if (pDevExt->MouExt.uCurrSignAndOverflow & X_DATA_SIGN)
pDevExt->MouExt.CurrentInput.LastX |= MOUSE_MAXIMUM_NEGATIVE_DELTA;
}
pDevExt->MouExt.InputState = YMovement;
break;
}
case YMovement:
{
if (pDevExt->MouExt.uCurrSignAndOverflow & Y_OVERFLOW)
{
uPrevSignAndOverflow = pDevExt->MouExt.uPrevSignAndOverflow;
if (uPrevSignAndOverflow & Y_OVERFLOW)
{
if ((uPrevSignAndOverflow & Y_DATA_SIGN) != (pDevExt->MouExt.uCurrSignAndOverflow & Y_DATA_SIGN))
pDevExt->MouExt.uCurrSignAndOverflow ^= Y_DATA_SIGN;
}
if (pDevExt->MouExt.uCurrSignAndOverflow & Y_DATA_SIGN)
pDevExt->MouExt.CurrentInput.LastY = MOUSE_MAXIMUM_POSITIVE_DELTA;
else
pDevExt->MouExt.CurrentInput.LastY = MOUSE_MAXIMUM_NEGATIVE_DELTA;
}
else
{
pDevExt->MouExt.CurrentInput.LastY = (ULONG) byte;
if (pDevExt->MouExt.uCurrSignAndOverflow & Y_DATA_SIGN)
pDevExt->MouExt.CurrentInput.LastY |= MOUSE_MAXIMUM_NEGATIVE_DELTA;
pDevExt->MouExt.CurrentInput.LastY = -pDevExt->MouExt.CurrentInput.LastY;
}
pDevExt->MouExt.uPrevSignAndOverflow = pDevExt->MouExt.uCurrSignAndOverflow;
if (pDevExt->HardwarePresent & WHEELMOUSE_HARDWARE_PRESENT)
pDevExt->MouExt.InputState = ZMovement;
else
{
pDevExt->MouExt.CurrentInput.Flags = MOUSE_MOVE_RELATIVE;
{
VMMDevReqMouseStatus *pReq = pDevExt->pReq;
if (pReq)
{
int rc = VbglGRPerform (&pReq->header);
if (RT_SUCCESS(rc))
{
if (pReq->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE)
{
/* make it an absolute move */
pDevExt->MouExt.CurrentInput.Flags = MOUSE_MOVE_ABSOLUTE;
pDevExt->MouExt.CurrentInput.LastX = pReq->pointerXPos;
pDevExt->MouExt.CurrentInput.LastY = pReq->pointerYPos;
}
}
else
Log(("VBoxMouseNT: ERROR querying mouse capabilities from VMMDev. rc = %Rrc\n", rc));
}
}
QueueInput(pDevObj);
pDevExt->MouExt.InputState = MouseIdle;
}
break;
}
case ZMovement:
{
#if 0
if (byte && pDevExt->MouExt.CurrentInput.Buttons == 0)
#else
if (byte)
#endif
{
if (byte & 0x80)
pDevExt->MouExt.CurrentInput.ButtonData = 0x0078;
else
pDevExt->MouExt.CurrentInput.ButtonData = 0xFF88;
pDevExt->MouExt.CurrentInput.ButtonFlags |= MOUSE_WHEEL;
}
pDevExt->MouExt.CurrentInput.Flags = MOUSE_MOVE_RELATIVE;
{
VMMDevReqMouseStatus *pReq = pDevExt->pReq;
if (pReq)
{
int rc = VbglGRPerform(&pReq->header);
if (RT_SUCCESS(rc))
{
if (pReq->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE)
{
/* make it an absolute move */
pDevExt->MouExt.CurrentInput.Flags = MOUSE_MOVE_ABSOLUTE;
pDevExt->MouExt.CurrentInput.LastX = pReq->pointerXPos;
pDevExt->MouExt.CurrentInput.LastY = pReq->pointerYPos;
}
}
else
Log(("VBoxMouseNT: ERROR querying mouse capabilities from VMMDev. rc = %Rrc\n", rc));
}
}
QueueInput(pDevObj);
pDevExt->MouExt.InputState = MouseIdle;
break;
}
case MouseExpectingACK:
{
if (byte == ACKNOWLEDGE)
pDevExt->MouExt.InputState = MouseIdle;
else if (byte == RESEND)
{
PutByteAsync(i8042Cmd, pDevExt, I8042_WRITE_TO_AUXILIARY_DEVICE);
PutByteAsync(i8042Dat, pDevExt, ENABLE_MOUSE_TRANSMISSION);
}
break;
}
default:
{
ASSERT(FALSE);
break;
}
}
return TRUE;
}
/**
* Interrupt service routine.
*/
static BOOLEAN KbdIntHandler(PKINTERRUPT Interrupt, PVOID pCtx)
{
NOREF(Interrupt);
PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pCtx;
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
if ((I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd]) & (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
!= OUTPUT_BUFFER_FULL)
{
for (unsigned i = 0; i < (ULONG)pDevExt->Cfg.PollStatusIterations; i++)
{
KeStallExecutionProcessor(1);
if ((I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd]) & (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
== (OUTPUT_BUFFER_FULL))
break;
}
if ((I8X_GET_STATUS_BYTE(pDevExt->DevRegs[i8042Cmd]) & (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
!= (OUTPUT_BUFFER_FULL))
{
if (pDevExt->KeyboardEnableCount == 0)
UCHAR scanCode = I8X_GET_DATA_BYTE(pDevExt->DevRegs[i8042Dat]);
return FALSE;
}
}
UCHAR scanCode;
GetByteAsync(KbdDevType, pDevExt, &scanCode);
switch (scanCode)
{
case RESEND:
if (pDevExt->TimerCount == 0)
break;
pDevExt->TimerCount = -1;
if ( pDevExt->KbdExt.CurrentOutput.State==Idle
|| !pDevObj->CurrentIrp)
goto ScanCodeCase;
else if (pDevExt->KbdExt.ResendCount < pDevExt->Cfg.iResend)
{
pDevExt->KbdExt.ResendCount++;
KbdStartIO(pDevObj);
}
else
{
pDevExt->KbdExt.CurrentOutput.State = Idle;
KeInsertQueueDpc(&pDevExt->RetriesExceededDpc, pDevObj->CurrentIrp, NULL);
}
break;
case ACKNOWLEDGE:
if (pDevExt->TimerCount == 0)
break;
pDevExt->TimerCount = -1;
pDevExt->KbdExt.ResendCount = 0;
if (pDevExt->KbdExt.CurrentOutput.State == SendFirstByte)
{
pDevExt->KbdExt.CurrentOutput.State = SendLastByte;
KbdStartIO(pDevObj);
}
else if (pDevExt->KbdExt.CurrentOutput.State == SendLastByte)
{
pDevExt->KbdExt.CurrentOutput.State = Idle;
IoRequestDpc(pDevObj, pDevObj->CurrentIrp, NULL);
}
break;
ScanCodeCase:
default:
{
PKEYBOARD_INPUT_DATA input = &pDevExt->KbdExt.CurrentInput;
KBDSCANSTATE *pScanState = &pDevExt->KbdExt.CurrentScanState;
if (scanCode == (UCHAR) 0xFF)
{
input->MakeCode = KEYBOARD_OVERRUN_MAKE_CODE;
input->Flags = 0;
*pScanState = Normal;
}
else
{
switch (*pScanState)
{
case Normal:
if (scanCode == (UCHAR) 0xE0)
{
input->Flags |= KEY_E0;
*pScanState = GotE0;
break;
}
else if (scanCode == (UCHAR) 0xE1)
{
input->Flags |= KEY_E1;
*pScanState = GotE1;
break;
}
/* fall through */
case GotE0:
case GotE1:
if (scanCode > 0x7F)
{
input->MakeCode = scanCode & 0x7F;
input->Flags |= KEY_BREAK;
}
else
input->MakeCode = scanCode;
*pScanState = Normal;
break;
default:
ASSERT(FALSE);
break;
}
}
if (*pScanState == Normal)
{
if (pDevExt->KeyboardEnableCount)
{
pDevExt->KbdExt.CurrentInput.UnitId = pDevExt->KbdExt.UnitId;
if (!KbdDataToQueue(&pDevExt->KbdExt, input))
{
}
else if (pDevExt->DpcInterlockKeyboard >= 0)
pDevExt->DpcInterlockKeyboard++;
else
KeInsertQueueDpc(&pDevExt->KeyboardIsrDpc, pDevObj->CurrentIrp, NULL);
}
input->Flags = 0;
}
break;
}
}
return TRUE;
}
static NTSTATUS MouEnableTrans(PDEVICE_OBJECT pDevObj)
{
NTSTATUS status;
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
status = PutBytePoll(i8042Dat, FALSE /*=wait*/, MouDevType, pDevExt, ENABLE_MOUSE_TRANSMISSION);
return status;
}
/**
* Configuration information for the keyboard.
*/
static VOID KbdGetRegstry(PINITEXT pInit, PUNICODE_STRING RegistryPath,
PUNICODE_STRING KeyboardDeviceName, PUNICODE_STRING PointerDeviceName)
{
PDEVEXT pDevExt = &pInit->DevExt;
for (unsigned i = 0; i < MaximumInterfaceType; i++)
{
INTERFACE_TYPE interfaceType = (INTERFACE_TYPE)i;
CONFIGURATION_TYPE controllerType = KeyboardController;
CONFIGURATION_TYPE peripheralType = KeyboardPeripheral;
NTSTATUS status = IoQueryDeviceDescription(&interfaceType, NULL,
&controllerType, NULL,
&peripheralType, NULL,
KbdCallOut, pInit);
if (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
{
HwGetRegstry(pInit, RegistryPath, KeyboardDeviceName, PointerDeviceName);
PI8042CFGINF pCfg = &pInit->DevExt.Cfg;
PKEYBOARD_ID keyboardId = &pCfg->KbdAttr.KeyboardIdentifier;
if (!ENHANCED_KEYBOARD(*keyboardId))
pCfg->PollingIterations = pCfg->PollingIterationsMaximum;
pCfg->KbdAttr.NumberOfFunctionKeys = s_aKeybType[keyboardId->Type-1].cFunctionKeys;
pCfg->KbdAttr.NumberOfIndicators = s_aKeybType[keyboardId->Type-1].cIndicators;
pCfg->KbdAttr.NumberOfKeysTotal = s_aKeybType[keyboardId->Type-1].cKeysTotal;
pCfg->KbdAttr.KeyboardMode = 1;
pCfg->KbdAttr.KeyRepeatMinimum.Rate = 2;
pCfg->KbdAttr.KeyRepeatMinimum.Delay = 250;
pCfg->KbdAttr.KeyRepeatMaximum.Rate = 30;
pCfg->KbdAttr.KeyRepeatMaximum.Delay = 1000;
pCfg->KeyRepeatCurrent.Rate = 30;
pCfg->KeyRepeatCurrent.Delay = 250;
break;
}
}
}
/**
* Retrieve the configuration information for the mouse.
*/
static VOID MouGetRegstry(PINITEXT pInit, PUNICODE_STRING RegistryPath,
PUNICODE_STRING KeyboardDeviceName, PUNICODE_STRING PointerDeviceName)
{
PDEVEXT pDevExt = &pInit->DevExt;
NTSTATUS status = STATUS_SUCCESS;
INTERFACE_TYPE interfaceType;
CONFIGURATION_TYPE controllerType = PointerController;
CONFIGURATION_TYPE peripheralType = PointerPeripheral;
for (unsigned i = 0; i < MaximumInterfaceType; i++)
{
interfaceType = (INTERFACE_TYPE)i;
status = IoQueryDeviceDescription(&interfaceType, NULL,
&controllerType, NULL,
&peripheralType, NULL,
MouCallOut, pInit);
if (pInit->DevExt.HardwarePresent & MOUSE_HARDWARE_PRESENT)
{
if (!(pInit->DevExt.HardwarePresent & KEYBOARD_HARDWARE_PRESENT))
HwGetRegstry(pInit, RegistryPath, KeyboardDeviceName, PointerDeviceName);
pInit->DevExt.Cfg.MouAttr.MouseIdentifier = MOUSE_I8042_HARDWARE;
break;
}
}
}
/**
* Initialize the driver.
*/
NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING RegistryPath)
{
PDEVICE_OBJECT pPortDevObj = NULL;
NTSTATUS status = STATUS_SUCCESS;
KIRQL IrqlCoord = 0;
ULONG IntVecKbd;
ULONG IntVecMou;
KIRQL IrqlKbd;
KIRQL IrqlMou;
KAFFINITY AffKbd;
KAFFINITY AffMou;
ULONG addressSpace;
PHYSICAL_ADDRESS Phys;
BOOLEAN fConflict;
UNICODE_STRING KbdNameFull;
UNICODE_STRING MouNameFull;
UNICODE_STRING KbdNameBase;
UNICODE_STRING MouNameBase;
UNICODE_STRING DevNameSuff;
UNICODE_STRING resourceDeviceClass;
UNICODE_STRING registryPath;
#define NAME_MAX 256
WCHAR keyboardBuffer[NAME_MAX];
WCHAR pointerBuffer[NAME_MAX];
LogFlow(("VBoxMouseNT::DriverEntry: enter\n"));
PINITEXT pInit = (PINITEXT)ExAllocatePool(NonPagedPool, sizeof(INITEXT));
if (!pInit)
{
status = STATUS_UNSUCCESSFUL;
goto fail;
}
RtlZeroMemory(pInit, sizeof(INITEXT));
KbdNameFull.MaximumLength = 0;
KbdNameFull.Length = 0;
MouNameFull.MaximumLength = 0;
MouNameFull.Length = 0;
DevNameSuff.MaximumLength = 0;
DevNameSuff.Length = 0;
resourceDeviceClass.MaximumLength = 0;
resourceDeviceClass.Length = 0;
registryPath.MaximumLength = 0;
RtlZeroMemory(keyboardBuffer, NAME_MAX * sizeof(WCHAR));
KbdNameBase.Buffer = keyboardBuffer;
KbdNameBase.Length = 0;
KbdNameBase.MaximumLength = NAME_MAX * sizeof(WCHAR);
RtlZeroMemory(pointerBuffer, NAME_MAX * sizeof(WCHAR));
MouNameBase.Buffer = pointerBuffer;
MouNameBase.Length = 0;
MouNameBase.MaximumLength = NAME_MAX * sizeof(WCHAR);
registryPath.Buffer = (PWSTR)ExAllocatePool(PagedPool, RegistryPath->Length + sizeof(UNICODE_NULL));
if (!registryPath.Buffer)
{
status = STATUS_UNSUCCESSFUL;
goto fail;
}
else
{
registryPath.Length = RegistryPath->Length + sizeof(UNICODE_NULL);
registryPath.MaximumLength = registryPath.Length;
RtlZeroMemory(registryPath.Buffer, registryPath.Length);
RtlMoveMemory(registryPath.Buffer, RegistryPath->Buffer, RegistryPath->Length);
}
KbdGetRegstry(pInit, &registryPath, &KbdNameBase, &MouNameBase);
MouGetRegstry(pInit, &registryPath, &KbdNameBase, &MouNameBase);
if (pInit->DevExt.HardwarePresent == 0)
{
status = STATUS_NO_SUCH_DEVICE;
goto fail;
}
else if (!(pInit->DevExt.HardwarePresent & KEYBOARD_HARDWARE_PRESENT))
status = STATUS_NO_SUCH_DEVICE;
RtlInitUnicodeString(&DevNameSuff, NULL);
DevNameSuff.MaximumLength = (KEYBOARD_PORTS_MAXIMUM > POINTER_PORTS_MAXIMUM)
? KEYBOARD_PORTS_MAXIMUM * sizeof(WCHAR)
: POINTER_PORTS_MAXIMUM * sizeof(WCHAR);
DevNameSuff.MaximumLength += sizeof(UNICODE_NULL);
DevNameSuff.Buffer = (PWSTR)ExAllocatePool(PagedPool, DevNameSuff.MaximumLength);
if (!DevNameSuff.Buffer)
{
status = STATUS_UNSUCCESSFUL;
goto fail;
}
RtlZeroMemory(DevNameSuff.Buffer, DevNameSuff.MaximumLength);
RtlInitUnicodeString(&KbdNameFull, NULL);
KbdNameFull.MaximumLength = sizeof(L"\\Device\\") + KbdNameBase.Length + DevNameSuff.MaximumLength;
KbdNameFull.Buffer = (PWSTR)ExAllocatePool(PagedPool, KbdNameFull.MaximumLength);
if (!KbdNameFull.Buffer)
{
status = STATUS_UNSUCCESSFUL;
goto fail;
}
RtlZeroMemory(KbdNameFull.Buffer, KbdNameFull.MaximumLength);
RtlAppendUnicodeToString(&KbdNameFull, L"\\Device\\");
RtlAppendUnicodeToString(&KbdNameFull, KbdNameBase.Buffer);
for (unsigned i = 0; i < KEYBOARD_PORTS_MAXIMUM; i++)
{
status = RtlIntegerToUnicodeString(i, 10, &DevNameSuff);
if (!NT_SUCCESS(status))
break;
RtlAppendUnicodeStringToString(&KbdNameFull, &DevNameSuff);
LogFlow(("VBoxMouseNT::DriverEntry: Creating device object named %S\n", KbdNameFull.Buffer));
status = IoCreateDevice(pDrvObj, sizeof(DEVEXT), &KbdNameFull,
FILE_DEVICE_8042_PORT, 0, FALSE, &pPortDevObj);
if (NT_SUCCESS(status))
break;
else
KbdNameFull.Length -= DevNameSuff.Length;
}
if (!NT_SUCCESS(status))
goto fail;
PDEVEXT pDevExt = (PDEVEXT)pPortDevObj->DeviceExtension;
*pDevExt = pInit->DevExt;
pDevExt->pDevObj = pPortDevObj;
ULONG resourceListSize = 0;
PCM_RESOURCE_LIST resources = NULL;
CreateResList(pDevExt, &resources, &resourceListSize);
RtlInitUnicodeString(&resourceDeviceClass, NULL);
resourceDeviceClass.MaximumLength = KbdNameBase.Length + sizeof(L"/") + MouNameBase.Length;
resourceDeviceClass.Buffer = (PWSTR)ExAllocatePool(PagedPool, resourceDeviceClass.MaximumLength);
if (!resourceDeviceClass.Buffer)
{
status = STATUS_UNSUCCESSFUL;
goto fail;
}
RtlZeroMemory(resourceDeviceClass.Buffer, resourceDeviceClass.MaximumLength);
RtlAppendUnicodeStringToString(&resourceDeviceClass, &KbdNameBase);
RtlAppendUnicodeToString(&resourceDeviceClass, L"/");
RtlAppendUnicodeStringToString(&resourceDeviceClass, &MouNameBase);
IoReportResourceUsage(&resourceDeviceClass, pDrvObj, NULL, 0, pPortDevObj,
resources, resourceListSize, FALSE, &fConflict);
if (fConflict)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fail;
}
for (unsigned i = 0; i < pDevExt->Cfg.cPorts; i++)
{
addressSpace = (pDevExt->Cfg.aPorts[i].Flags & CM_RESOURCE_PORT_IO) == CM_RESOURCE_PORT_IO ? 1 : 0;
if (!HalTranslateBusAddress(pDevExt->Cfg.InterfaceType,
pDevExt->Cfg.uBusNr,
pDevExt->Cfg.aPorts[i].u.Port.Start,
&addressSpace, &Phys))
{
addressSpace = 1;
Phys.QuadPart = 0;
}
if (!addressSpace)
{
pDevExt->fUnmapRegs = TRUE;
pDevExt->DevRegs[i] = (PUCHAR)MmMapIoSpace(Phys, pDevExt->Cfg.aPorts[i].u.Port.Length,
(MEMORY_CACHING_TYPE)FALSE);
}
else
{
pDevExt->fUnmapRegs = FALSE;
pDevExt->DevRegs[i] = (PUCHAR)Phys.LowPart;
}
if (!pDevExt->DevRegs[i])
{
status = STATUS_NONE_MAPPED;
goto fail;
}
}
pPortDevObj->Flags |= DO_BUFFERED_IO;
InitHw(pPortDevObj);
KeInitializeSpinLock(&pDevExt->ShIntObj);
if (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
{
pDevExt->KbdExt.InputData = (PKEYBOARD_INPUT_DATA)
ExAllocatePool(NonPagedPool, pDevExt->Cfg.KbdAttr.InputDataQueueLength);
if (!pDevExt->KbdExt.InputData)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fail;
}
pDevExt->KbdExt.DataEnd =
(PKEYBOARD_INPUT_DATA)((PCHAR) (pDevExt->KbdExt.InputData) + pDevExt->Cfg.KbdAttr.InputDataQueueLength);
RtlZeroMemory(pDevExt->KbdExt.InputData, pDevExt->Cfg.KbdAttr.InputDataQueueLength);
}
if (pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT)
{
RtlInitUnicodeString(&MouNameFull, NULL);
MouNameFull.MaximumLength = sizeof(L"\\Device\\") + MouNameBase.Length + DevNameSuff.MaximumLength;
MouNameFull.Buffer = (PWSTR)ExAllocatePool(PagedPool, MouNameFull.MaximumLength);
if (!MouNameFull.Buffer)
{
status = STATUS_UNSUCCESSFUL;
goto fail;
}
RtlZeroMemory(MouNameFull.Buffer, MouNameFull.MaximumLength);
RtlAppendUnicodeToString(&MouNameFull, L"\\Device\\");
RtlAppendUnicodeToString(&MouNameFull, MouNameBase.Buffer);
RtlZeroMemory(DevNameSuff.Buffer, DevNameSuff.MaximumLength);
DevNameSuff.Length = 0;
for (unsigned i = 0; i < POINTER_PORTS_MAXIMUM; i++)
{
status = RtlIntegerToUnicodeString(i, 10, &DevNameSuff);
if (!NT_SUCCESS(status))
break;
RtlAppendUnicodeStringToString(&MouNameFull, &DevNameSuff);
LogFlow(("VBoxMouseNT::DriverEntry: pointer port name (symbolic link) = %S\n", MouNameFull.Buffer));
status = IoCreateSymbolicLink(&MouNameFull, &KbdNameFull);
if (NT_SUCCESS(status))
break;
else
MouNameFull.Length -= DevNameSuff.Length;
}
if (!NT_SUCCESS(status))
goto fail;
pDevExt->MouExt.InputData =
(PMOUSE_INPUT_DATA)ExAllocatePool(NonPagedPool, pDevExt->Cfg.MouAttr.InputDataQueueLength);
if (!pDevExt->MouExt.InputData)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto fail;
}
pDevExt->MouExt.DataEnd = (PMOUSE_INPUT_DATA)((PCHAR) (pDevExt->MouExt.InputData) + pDevExt->Cfg.MouAttr.InputDataQueueLength);
RtlZeroMemory(pDevExt->MouExt.InputData, pDevExt->Cfg.MouAttr.InputDataQueueLength);
}
pDevExt->KbdExt.ConnectData.ClassDeviceObject = NULL;
pDevExt->KbdExt.ConnectData.ClassService = NULL;
pDevExt->MouExt.ConnectData.ClassDeviceObject = NULL;
pDevExt->MouExt.ConnectData.ClassService = NULL;
I8042INITDATACTX initDataCtx;
initDataCtx.pDevExt = pDevExt;
initDataCtx.DevType = KbdDevType;
InitDataQueue(&initDataCtx);
initDataCtx.DevType = MouDevType;
InitDataQueue(&initDataCtx);
pDevExt->DpcInterlockKeyboard = -1;
pDevExt->DpcInterlockMouse = -1;
IoInitializeDpcRequest(pPortDevObj, CompleteDpc);
KeInitializeDpc(&pDevExt->RetriesExceededDpc, (PKDEFERRED_ROUTINE)CtrlRetriesExceededDpc, pPortDevObj);
KeInitializeDpc(&pDevExt->KeyboardIsrDpc, (PKDEFERRED_ROUTINE)CtrlKbdIsrDpc, pPortDevObj);
KeInitializeDpc(&pDevExt->KeyboardIsrDpcRetry, (PKDEFERRED_ROUTINE)CtrlKbdIsrDpc, pPortDevObj);
KeInitializeDpc(&pDevExt->MouseIsrDpc, (PKDEFERRED_ROUTINE)CtrlMouIsrDpc, pPortDevObj);
KeInitializeDpc(&pDevExt->MouseIsrDpcRetry, (PKDEFERRED_ROUTINE)CtrlMouIsrDpc, pPortDevObj);
KeInitializeDpc(&pDevExt->TimeOutDpc, (PKDEFERRED_ROUTINE)CtrlTimeoutDpc, pPortDevObj);
KeInitializeTimer(&pDevExt->CommandTimer);
pDevExt->TimerCount = -1;
KeInitializeTimer(&pDevExt->KbdExt.DataConsumptionTimer);
KeInitializeTimer(&pDevExt->MouExt.DataConsumptionTimer);
IntVecKbd = HalGetInterruptVector(pDevExt->Cfg.InterfaceType,
pDevExt->Cfg.uBusNr,
pDevExt->Cfg.KbdInt.u.Interrupt.Level,
pDevExt->Cfg.KbdInt.u.Interrupt.Vector,
&IrqlKbd, &AffKbd);
IntVecMou = HalGetInterruptVector(pDevExt->Cfg.InterfaceType,
pDevExt->Cfg.uBusNr,
pDevExt->Cfg.MouInt.u.Interrupt.Level,
pDevExt->Cfg.MouInt.u.Interrupt.Vector,
&IrqlMou, &AffMou);
if ( (pDevExt->HardwarePresent & (KEYBOARD_HARDWARE_PRESENT | MOUSE_HARDWARE_PRESENT))
== (KEYBOARD_HARDWARE_PRESENT | MOUSE_HARDWARE_PRESENT))
IrqlCoord = IrqlKbd > IrqlMou ? IrqlKbd : IrqlMou;
if (pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT)
{
status = IoConnectInterrupt(&pDevExt->MouIntObj, MouIntHandler, pPortDevObj,
&pDevExt->ShIntObj, IntVecMou, IrqlMou,
(KIRQL) ((IrqlCoord == (KIRQL)0) ? IrqlMou : IrqlCoord),
pDevExt->Cfg.MouInt.Flags == CM_RESOURCE_INTERRUPT_LATCHED
? Latched : LevelSensitive,
pDevExt->Cfg.MouInt.ShareDisposition,
AffMou, pDevExt->Cfg.fFloatSave);
if (!NT_SUCCESS(status))
goto fail;
status = MouEnableTrans(pPortDevObj);
if (!NT_SUCCESS(status))
status = STATUS_SUCCESS;
}
if (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
{
status = IoConnectInterrupt(&pDevExt->KbdIntObj, KbdIntHandler, pPortDevObj,
&pDevExt->ShIntObj, IntVecKbd, IrqlKbd,
(KIRQL) ((IrqlCoord == (KIRQL)0) ? IrqlKbd : IrqlCoord),
pDevExt->Cfg.KbdInt.Flags == CM_RESOURCE_INTERRUPT_LATCHED
? Latched : LevelSensitive,
pDevExt->Cfg.KbdInt.ShareDisposition,
AffKbd, pDevExt->Cfg.fFloatSave);
if (!NT_SUCCESS(status))
goto fail;
}
if (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
{
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
KbdNameBase.Buffer, KbdNameFull.Buffer,
REG_SZ,
registryPath.Buffer, registryPath.Length);
if (!NT_SUCCESS(status))
goto fail;
}
if (pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT)
{
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
MouNameBase.Buffer, MouNameFull.Buffer,
REG_SZ,
registryPath.Buffer, registryPath.Length);
if (!NT_SUCCESS(status))
goto fail;
}
ASSERT(status == STATUS_SUCCESS);
int rcVBox = VbglInit();
if (RT_FAILURE(rcVBox))
{
Log(("VBoxMouseNT::DriverEntry: could not initialize guest library, rc = %Rrc\n", rcVBox));
/* Continue working in non-VBox mode. */
}
else
{
VMMDevReqMouseStatus *pReq = NULL;
rcVBox = VbglGRAlloc((VMMDevRequestHeader**)&pReq, sizeof(VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
if (RT_SUCCESS(rcVBox))
{
/* Inform host that we support absolute */
pReq->mouseFeatures = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
pReq->pointerXPos = 0;
pReq->pointerYPos = 0;
rcVBox = VbglGRPerform(&pReq->header);
if (RT_FAILURE(rcVBox))
Log(("VBoxMouseNT::DriverEntry: ERROR communicating new mouse capabilities to VMMDev. rc = %Rrc\n", rcVBox));
else
{
/* We will use the allocated request buffer in the ServiceCallback to GET mouse status. */
pReq->header.requestType = VMMDevReq_GetMouseStatus;
pDevExt->pReq = pReq;
}
}
else
{
VbglTerminate();
Log(("VBoxMouseNT::DriverEntry: could not allocate request buffer, rc = %Rrc\n", rcVBox));
/* Continue working in non-VBox mode. */
}
}
pDrvObj->DriverStartIo = I8042StartIo;
pDrvObj->MajorFunction[IRP_MJ_CREATE] = I8042OpenClose;
pDrvObj->MajorFunction[IRP_MJ_CLOSE] = I8042OpenClose;
pDrvObj->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = I8042Flush;
pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = I8042DevCtrl;
fail:
if (!NT_SUCCESS(status))
{
if (resources)
{
resources->Count = 0;
IoReportResourceUsage(&resourceDeviceClass, pDrvObj, NULL,
0, pPortDevObj, resources, resourceListSize, FALSE, &fConflict);
}
if (pDevExt)
{
if (pDevExt->KbdIntObj)
IoDisconnectInterrupt(pDevExt->KbdIntObj);
if (pDevExt->MouIntObj)
IoDisconnectInterrupt(pDevExt->MouIntObj);
if (pDevExt->KbdExt.InputData)
ExFreePool(pDevExt->KbdExt.InputData);
if (pDevExt->MouExt.InputData)
ExFreePool(pDevExt->MouExt.InputData);
if (pDevExt->fUnmapRegs)
{
for (unsigned i = 0; i < pDevExt->Cfg.cPorts; i++)
if (pDevExt->DevRegs[i])
MmUnmapIoSpace(pDevExt->DevRegs[i], pDevExt->Cfg.aPorts[i].u.Port.Length);
}
}
if (pPortDevObj)
{
if (MouNameFull.Length > 0)
IoDeleteSymbolicLink(&MouNameFull);
IoDeleteDevice(pPortDevObj);
}
}
if (resources)
ExFreePool(resources);
if (pInit)
ExFreePool(pInit);
if (DevNameSuff.MaximumLength)
ExFreePool(DevNameSuff.Buffer);
if (KbdNameFull.MaximumLength)
ExFreePool(KbdNameFull.Buffer);
if (MouNameFull.MaximumLength)
ExFreePool(MouNameFull.Buffer);
if (resourceDeviceClass.MaximumLength)
ExFreePool(resourceDeviceClass.Buffer);
if (registryPath.MaximumLength)
ExFreePool(registryPath.Buffer);
LogFlow(("VBoxMouseNT::DriverEntry: leave, status = %d\n", status));
return status;
}
static VOID I8042Unload(PDRIVER_OBJECT pDrvObj)
{
NOREF(pDrvObj);
}
/**
* Build a resource list.
*/
static VOID CreateResList(PDEVEXT pDevExt, PCM_RESOURCE_LIST *pResList, PULONG pResListSize)
{
ULONG cPorts = pDevExt->Cfg.cPorts;
if (pDevExt->Cfg.KbdInt.Type == CmResourceTypeInterrupt)
cPorts++;
if (pDevExt->Cfg.MouInt.Type == CmResourceTypeInterrupt)
cPorts++;
*pResListSize = sizeof(CM_RESOURCE_LIST) + ((cPorts - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
*pResList = (PCM_RESOURCE_LIST)ExAllocatePool(PagedPool, *pResListSize);
if (!*pResList)
{
*pResListSize = 0;
return;
}
RtlZeroMemory(*pResList, *pResListSize);
(*pResList)->Count = 1;
(*pResList)->List[0].InterfaceType = pDevExt->Cfg.InterfaceType;
(*pResList)->List[0].BusNumber = pDevExt->Cfg.uBusNr;
(*pResList)->List[0].PartialResourceList.Count = cPorts;
ULONG i = 0;
if (pDevExt->Cfg.KbdInt.Type == CmResourceTypeInterrupt)
(*pResList)->List[0].PartialResourceList.PartialDescriptors[i++] = pDevExt->Cfg.KbdInt;
if (pDevExt->Cfg.MouInt.Type == CmResourceTypeInterrupt)
(*pResList)->List[0].PartialResourceList.PartialDescriptors[i++] = pDevExt->Cfg.MouInt;
for (unsigned j = 0; j < pDevExt->Cfg.cPorts; j++)
(*pResList)->List[0].PartialResourceList.PartialDescriptors[i++] = pDevExt->Cfg.aPorts[j];
}
/**
* Read the i8042 controller command byte
*/
static NTSTATUS GetCtrlCmd(ULONG HwDisEnMask, PDEVEXT pDevExt, PUCHAR pByte)
{
NTSTATUS status;
if (HwDisEnMask & KEYBOARD_HARDWARE_PRESENT)
{
status = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_DISABLE_KEYBOARD_DEVICE);
if (!NT_SUCCESS(status))
return status;
}
if (HwDisEnMask & MOUSE_HARDWARE_PRESENT)
{
status = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_DISABLE_MOUSE_DEVICE);
if (!NT_SUCCESS(status))
{
if (HwDisEnMask & KEYBOARD_HARDWARE_PRESENT)
PutBytePoll(i8042Cmd, FALSE/*=wait*/, NoDevice, pDevExt, I8042_ENABLE_KEYBOARD_DEVICE);
return status;
}
}
status = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_READ_CONTROLLER_COMMAND_BYTE);
if (NT_SUCCESS(status))
{
for (unsigned iRetry = 0; iRetry < 5; iRetry++)
{
status = GetBytePoll(CtrlDevType, pDevExt, pByte);
if (NT_SUCCESS(status))
break;
if (status == STATUS_IO_TIMEOUT)
KeStallExecutionProcessor(50);
else
break;
}
}
NTSTATUS status2;
if (HwDisEnMask & KEYBOARD_HARDWARE_PRESENT)
{
status2 = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_ENABLE_KEYBOARD_DEVICE);
if (!NT_SUCCESS(status2))
{
if (NT_SUCCESS(status))
status = status2;
}
else if (status == STATUS_SUCCESS)
*pByte &= (UCHAR)~CCB_DISABLE_KEYBOARD_DEVICE;
}
if (HwDisEnMask & MOUSE_HARDWARE_PRESENT)
{
status2 = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_ENABLE_MOUSE_DEVICE);
if (!NT_SUCCESS(status2))
{
if (NT_SUCCESS(status))
status = status2;
}
else if (NT_SUCCESS(status))
*pByte &= (UCHAR)~CCB_DISABLE_MOUSE_DEVICE;
}
return status;
}
/**
* Write the i8042 controller command byte.
*/
static NTSTATUS PutCtrlCmd(PDEVEXT pDevExt, UCHAR Byte)
{
NTSTATUS status;
status = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_WRITE_CONTROLLER_COMMAND_BYTE);
if (!NT_SUCCESS(status))
return status;
return (PutBytePoll(i8042Dat, FALSE /*=wait*/, NoDevice, pDevExt, Byte));
}
/**
* Read the i8042 controller command byte.
*/
static VOID TransCtrlCmd(PDEVEXT pDevExt, PI8042TRANSMITCCBCTX pCtx)
{
UCHAR bCtrlCmd;
pCtx->Status = GetCtrlCmd(pCtx->HwDisEnMask, pDevExt, &bCtrlCmd);
if (!NT_SUCCESS(pCtx->Status))
return;
if (pCtx->fAndOp)
bCtrlCmd &= pCtx->ByteMask;
else
bCtrlCmd |= pCtx->ByteMask;
pCtx->Status = PutCtrlCmd(pDevExt, bCtrlCmd);
UCHAR bVrfyCmd;
pCtx->Status = GetCtrlCmd(pCtx->HwDisEnMask, pDevExt, &bVrfyCmd);
if ( NT_SUCCESS(pCtx->Status)
&& bVrfyCmd != bCtrlCmd)
pCtx->Status = STATUS_DEVICE_DATA_ERROR;
}
/**
* Detect the number of mouse buttons.
*/
static NTSTATUS MouQueryButtons(PDEVICE_OBJECT pDevObj, PUCHAR pNumButtons)
{
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
NTSTATUS status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, SET_MOUSE_RESOLUTION);
if (!NT_SUCCESS(status))
return status;
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, 0x00);
if (!NT_SUCCESS(status))
return status;
for (unsigned i = 0; i < 3; i++)
{
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, SET_MOUSE_SCALING_1TO1);
if (!NT_SUCCESS(status))
return status;
}
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, READ_MOUSE_STATUS);
if (!NT_SUCCESS(status))
return status;
UCHAR byte;
status = GetBytePoll(CtrlDevType, pDevExt, &byte);
if (!NT_SUCCESS(status))
return status;
UCHAR buttons;
status = GetBytePoll(CtrlDevType, pDevExt, &buttons);
if (!NT_SUCCESS(status))
return status;
status = GetBytePoll(CtrlDevType, pDevExt, &byte);
if (!NT_SUCCESS(status))
return status;
if (buttons == 2 || buttons == 3)
*pNumButtons = buttons;
else
*pNumButtons = 0;
return status;
}
/**
* Initialize the i8042 mouse hardware.
*/
static NTSTATUS MouInitHw(PDEVICE_OBJECT pDevObj)
{
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
NTSTATUS status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, MOUSE_RESET);
if (!NT_SUCCESS(status))
goto fail;
UCHAR byte;
for (unsigned i = 0; i < 11200; i++)
{
status = GetBytePoll(CtrlDevType, pDevExt, &byte);
if (NT_SUCCESS(status) && byte == (UCHAR) MOUSE_COMPLETE)
break;
if (status != STATUS_IO_TIMEOUT)
break;
KeStallExecutionProcessor(50);
}
if (!NT_SUCCESS(status))
goto fail;
status = GetBytePoll(CtrlDevType, pDevExt, &byte);
if ((!NT_SUCCESS(status)) || (byte != MOUSE_ID_BYTE))
goto fail;
MouFindWheel(pDevObj);
UCHAR numButtons;
status = MouQueryButtons(pDevObj, &numButtons);
if (!NT_SUCCESS(status))
goto fail;
else if (numButtons)
pDevExt->Cfg.MouAttr.NumberOfButtons = numButtons;
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, SET_MOUSE_SAMPLING_RATE);
if (!NT_SUCCESS(status))
goto fail;
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, 60);
if (!NT_SUCCESS(status))
goto fail;
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, SET_MOUSE_RESOLUTION);
if (!NT_SUCCESS(status))
goto fail;
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, (UCHAR)pDevExt->Cfg.MouseResolution);
fail:
pDevExt->MouExt.uPrevSignAndOverflow = 0;
pDevExt->MouExt.InputState = MouseExpectingACK;
pDevExt->MouExt.LastByteReceived = 0;
return status;
}
/**
* Initialize the i8042 keyboard hardware.
*/
static NTSTATUS KbdInitHw(PDEVICE_OBJECT pDevObj)
{
NTSTATUS status;
BOOLEAN fWaitForAck = TRUE;
PDEVEXT pDevExt = (PDEVEXT)pDevObj->DeviceExtension;
retry:
PutBytePoll(i8042Dat, fWaitForAck, KbdDevType, pDevExt, KEYBOARD_RESET);
LARGE_INTEGER startOfSpin;
KeQueryTickCount(&startOfSpin);
for (unsigned i = 0; i < 11200; i++)
{
UCHAR byte;
status = GetBytePoll(KbdDevType, pDevExt, &byte);
if (NT_SUCCESS(status))
break;
else
{
if (status == STATUS_IO_TIMEOUT)
{
LARGE_INTEGER nextQuery, difference, tenSeconds;
KeStallExecutionProcessor(50);
KeQueryTickCount(&nextQuery);
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
tenSeconds.QuadPart = 10*10*1000*1000;
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
if (difference.QuadPart*KeQueryTimeIncrement() >= tenSeconds.QuadPart)
break;
}
else
break;
}
}
if (!NT_SUCCESS(status))
{
if (fWaitForAck)
{
fWaitForAck = FALSE;
goto retry;
}
goto fail;
}
I8042TRANSMITCCBCTX Ctx;
Ctx.HwDisEnMask = 0;
Ctx.fAndOp = TRUE;
Ctx.ByteMask = (UCHAR) ~((UCHAR)CCB_KEYBOARD_TRANSLATE_MODE);
TransCtrlCmd(pDevExt, &Ctx);
if (!NT_SUCCESS(Ctx.Status))
TransCtrlCmd(pDevExt, &Ctx);
if (!NT_SUCCESS(Ctx.Status))
{
status = Ctx.Status;
goto fail;
}
PKEYBOARD_ID pId = &pDevExt->Cfg.KbdAttr.KeyboardIdentifier;
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, KbdDevType, pDevExt, SET_KEYBOARD_TYPEMATIC);
if (status == STATUS_SUCCESS)
{
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, KbdDevType, pDevExt,
ConvertTypematic(pDevExt->Cfg.KeyRepeatCurrent.Rate,
pDevExt->Cfg.KeyRepeatCurrent.Delay));
/* ignore errors */
}
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, KbdDevType, pDevExt, SET_KEYBOARD_INDICATORS);
if (status == STATUS_SUCCESS)
{
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, KbdDevType, pDevExt,
(UCHAR)pDevExt->Cfg.KbdInd.LedFlags);
/* ignore errors */
}
status = STATUS_SUCCESS;
if (pDevExt->Cfg.KbdAttr.KeyboardMode == 1)
{
Ctx.HwDisEnMask = 0;
Ctx.fAndOp = FALSE;
Ctx.ByteMask = CCB_KEYBOARD_TRANSLATE_MODE;
TransCtrlCmd(pDevExt, &Ctx);
if (!NT_SUCCESS(Ctx.Status))
{
if (Ctx.Status == STATUS_DEVICE_DATA_ERROR)
{
if (ENHANCED_KEYBOARD(*pId))
{
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, KbdDevType, pDevExt, SELECT_SCAN_CODE_SET);
if (!NT_SUCCESS(status))
pDevExt->Cfg.KbdAttr.KeyboardMode = 2;
else
{
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, KbdDevType, pDevExt, 1);
if (!NT_SUCCESS(status))
pDevExt->Cfg.KbdAttr.KeyboardMode = 2;
}
}
}
else
{
status = Ctx.Status;
goto fail;
}
}
}
fail:
pDevExt->KbdExt.CurrentOutput.State = Idle;
pDevExt->KbdExt.CurrentOutput.FirstByte = 0;
pDevExt->KbdExt.CurrentOutput.LastByte = 0;
return status;
}
/**
* Initialize the i8042 controller, keyboard and mouse.
*/
static VOID InitHw(PDEVICE_OBJECT pDevObj)
{
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
PUCHAR dataAddress = pDevExt->DevRegs[i8042Dat];
PUCHAR commandAddress = pDevExt->DevRegs[i8042Cmd];
DrainOutBuf(dataAddress, commandAddress);
I8042TRANSMITCCBCTX Ctx;
Ctx.HwDisEnMask = 0;
Ctx.fAndOp = TRUE;
Ctx.ByteMask = (UCHAR) ~((UCHAR)CCB_ENABLE_KEYBOARD_INTERRUPT | (UCHAR)CCB_ENABLE_MOUSE_INTERRUPT);
TransCtrlCmd(pDevExt, &Ctx);
if (!NT_SUCCESS(Ctx.Status))
return;
DrainOutBuf(dataAddress, commandAddress);
if (pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT)
{
NTSTATUS status = MouInitHw(pDevObj);
if (!NT_SUCCESS(status))
pDevExt->HardwarePresent &= ~MOUSE_HARDWARE_PRESENT;
}
if (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
{
NTSTATUS status = KbdInitHw(pDevObj);
if (!NT_SUCCESS(status))
pDevExt->HardwarePresent &= ~KEYBOARD_HARDWARE_PRESENT;
}
if (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
{
NTSTATUS status = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_ENABLE_KEYBOARD_DEVICE);
if (!NT_SUCCESS(status))
pDevExt->HardwarePresent &= ~KEYBOARD_HARDWARE_PRESENT;
DrainOutBuf(dataAddress, commandAddress);
}
if (pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT)
{
NTSTATUS status = PutBytePoll(i8042Cmd, FALSE /*=wait*/, NoDevice, pDevExt, I8042_ENABLE_MOUSE_DEVICE);
if (!NT_SUCCESS(status))
pDevExt->HardwarePresent &= ~MOUSE_HARDWARE_PRESENT;
DrainOutBuf(dataAddress, commandAddress);
}
if (pDevExt->HardwarePresent)
{
Ctx.HwDisEnMask = pDevExt->HardwarePresent;
Ctx.fAndOp = FALSE;
Ctx.ByteMask = (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
? CCB_ENABLE_KEYBOARD_INTERRUPT : 0;
Ctx.ByteMask |= (UCHAR)
(pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT) ? CCB_ENABLE_MOUSE_INTERRUPT : 0;
TransCtrlCmd(pDevExt, &Ctx);
if (!NT_SUCCESS(Ctx.Status))
{
/* ignore */
}
}
}
/**
* retrieve the drivers service parameters from the registry
*/
static VOID HwGetRegstry(PINITEXT pInit, PUNICODE_STRING RegistryPath,
PUNICODE_STRING KeyboardDeviceName, PUNICODE_STRING PointerDeviceName)
{
PRTL_QUERY_REGISTRY_TABLE aQuery = NULL;
UNICODE_STRING parametersPath;
UNICODE_STRING defaultPointerName;
UNICODE_STRING defaultKeyboardName;
USHORT defaultResendIterations = 3;
ULONG iResend = 0;
USHORT defaultPollingIterations = 12000;
ULONG pollingIterations = 0;
USHORT defaultPollingIterationsMaximum = 12000;
ULONG pollingIterationsMaximum = 0;
USHORT defaultPollStatusIterations = 12000;
ULONG pollStatusIterations = 0;
ULONG defaultDataQueueSize = 100;
ULONG cButtons = 2;
USHORT cButtonsDef = 2;
ULONG sampleRate = 60;
USHORT defaultSampleRate = 60;
ULONG mouseResolution = 3;
USHORT defaultMouseResolution = 3;
ULONG overrideKeyboardType = 0;
ULONG invalidKeyboardType = 0;
ULONG overrideKeyboardSubtype = (ULONG)-1;
ULONG invalidKeyboardSubtype = (ULONG)-1;
ULONG defaultSynchPacket100ns = 10000000UL;
ULONG enableWheelDetection = 0;
ULONG defaultEnableWheelDetection = 1;
PWSTR path = NULL;
USHORT queries = 15;
PI8042CFGINF pCfg = &pInit->DevExt.Cfg;
NTSTATUS status = STATUS_SUCCESS;
pCfg->StallMicroseconds = 50;
parametersPath.Buffer = NULL;
path = RegistryPath->Buffer;
if (NT_SUCCESS(status))
{
aQuery = (PRTL_QUERY_REGISTRY_TABLE)
ExAllocatePool(PagedPool, sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1));
if (!aQuery)
status = STATUS_UNSUCCESSFUL;
else
{
RtlZeroMemory(aQuery, sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1));
RtlInitUnicodeString(&parametersPath, NULL);
parametersPath.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters");
parametersPath.Buffer = (PWSTR)ExAllocatePool(PagedPool, parametersPath.MaximumLength);
if (!parametersPath.Buffer)
status = STATUS_UNSUCCESSFUL;
}
}
if (NT_SUCCESS(status))
{
RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength);
RtlAppendUnicodeToString(&parametersPath, path);
RtlAppendUnicodeToString(&parametersPath, L"\\Parameters");
RtlInitUnicodeString(&defaultKeyboardName, L"KeyboardPort");
RtlInitUnicodeString(&defaultPointerName, L"PointerPort");
aQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[0].Name = L"iResend";
aQuery[0].EntryContext = &iResend;
aQuery[0].DefaultType = REG_DWORD;
aQuery[0].DefaultData = &defaultResendIterations;
aQuery[0].DefaultLength = sizeof(USHORT);
aQuery[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[1].Name = L"PollingIterations";
aQuery[1].EntryContext = &pollingIterations;
aQuery[1].DefaultType = REG_DWORD;
aQuery[1].DefaultData = &defaultPollingIterations;
aQuery[1].DefaultLength = sizeof(USHORT);
aQuery[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[2].Name = L"PollingIterationsMaximum";
aQuery[2].EntryContext = &pollingIterationsMaximum;
aQuery[2].DefaultType = REG_DWORD;
aQuery[2].DefaultData = &defaultPollingIterationsMaximum;
aQuery[2].DefaultLength = sizeof(USHORT);
aQuery[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[3].Name = L"KeyboardDataQueueSize";
aQuery[3].EntryContext = &pCfg->KbdAttr.InputDataQueueLength;
aQuery[3].DefaultType = REG_DWORD;
aQuery[3].DefaultData = &defaultDataQueueSize;
aQuery[3].DefaultLength = sizeof(ULONG);
aQuery[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[4].Name = L"MouseDataQueueSize";
aQuery[4].EntryContext = &pCfg->MouAttr.InputDataQueueLength;
aQuery[4].DefaultType = REG_DWORD;
aQuery[4].DefaultData = &defaultDataQueueSize;
aQuery[4].DefaultLength = sizeof(ULONG);
aQuery[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[5].Name = L"NumberOfButtons";
aQuery[5].EntryContext = &cButtons;
aQuery[5].DefaultType = REG_DWORD;
aQuery[5].DefaultData = &cButtonsDef;
aQuery[5].DefaultLength = sizeof(USHORT);
aQuery[6].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[6].Name = L"SampleRate";
aQuery[6].EntryContext = &sampleRate;
aQuery[6].DefaultType = REG_DWORD;
aQuery[6].DefaultData = &defaultSampleRate;
aQuery[6].DefaultLength = sizeof(USHORT);
aQuery[7].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[7].Name = L"MouseResolution";
aQuery[7].EntryContext = &mouseResolution;
aQuery[7].DefaultType = REG_DWORD;
aQuery[7].DefaultData = &defaultMouseResolution;
aQuery[7].DefaultLength = sizeof(USHORT);
aQuery[8].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[8].Name = L"OverrideKeyboardType";
aQuery[8].EntryContext = &overrideKeyboardType;
aQuery[8].DefaultType = REG_DWORD;
aQuery[8].DefaultData = &invalidKeyboardType;
aQuery[8].DefaultLength = sizeof(ULONG);
aQuery[9].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[9].Name = L"OverrideKeyboardSubtype";
aQuery[9].EntryContext = &overrideKeyboardSubtype;
aQuery[9].DefaultType = REG_DWORD;
aQuery[9].DefaultData = &invalidKeyboardSubtype;
aQuery[9].DefaultLength = sizeof(ULONG);
aQuery[10].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[10].Name = L"KeyboardDeviceBaseName";
aQuery[10].EntryContext = KeyboardDeviceName;
aQuery[10].DefaultType = REG_SZ;
aQuery[10].DefaultData = defaultKeyboardName.Buffer;
aQuery[10].DefaultLength = 0;
aQuery[11].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[11].Name = L"PointerDeviceBaseName";
aQuery[11].EntryContext = PointerDeviceName;
aQuery[11].DefaultType = REG_SZ;
aQuery[11].DefaultData = defaultPointerName.Buffer;
aQuery[11].DefaultLength = 0;
aQuery[12].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[12].Name = L"MouseSynchIn100ns";
aQuery[12].EntryContext = &pInit->DevExt.MouExt.SynchTickCount;
aQuery[12].DefaultType = REG_DWORD;
aQuery[12].DefaultData = &defaultSynchPacket100ns;
aQuery[12].DefaultLength = sizeof(ULONG);
aQuery[13].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[13].Name = L"PollStatusIterations";
aQuery[13].EntryContext = &pollStatusIterations;
aQuery[13].DefaultType = REG_DWORD;
aQuery[13].DefaultData = &defaultPollStatusIterations;
aQuery[13].DefaultLength = sizeof(USHORT);
aQuery[14].Flags = RTL_QUERY_REGISTRY_DIRECT;
aQuery[14].Name = L"EnableWheelDetection";
aQuery[14].EntryContext = &enableWheelDetection;
aQuery[14].DefaultType = REG_DWORD;
aQuery[14].DefaultData = &defaultEnableWheelDetection;
aQuery[14].DefaultLength = sizeof(ULONG);
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
parametersPath.Buffer, aQuery, NULL, NULL);
}
if (!NT_SUCCESS(status))
{
/* driver defaults */
pCfg->iResend = defaultResendIterations;
pCfg->PollingIterations = defaultPollingIterations;
pCfg->PollingIterationsMaximum = defaultPollingIterationsMaximum;
pCfg->PollStatusIterations = defaultPollStatusIterations;
pCfg->KbdAttr.InputDataQueueLength = defaultDataQueueSize;
pCfg->MouAttr.InputDataQueueLength = defaultDataQueueSize;
pCfg->EnableWheelDetection = defaultEnableWheelDetection;
pInit->DevExt.MouExt.SynchTickCount = defaultSynchPacket100ns;
RtlCopyUnicodeString(KeyboardDeviceName, &defaultKeyboardName);
RtlCopyUnicodeString(PointerDeviceName, &defaultPointerName);
}
else
{
pCfg->iResend = (USHORT)iResend;
pCfg->PollingIterations = (USHORT) pollingIterations;
pCfg->PollingIterationsMaximum = (USHORT) pollingIterationsMaximum;
pCfg->PollStatusIterations = (USHORT) pollStatusIterations;
pCfg->EnableWheelDetection = (ULONG) ((enableWheelDetection) ? 1 : 0);
}
if (pCfg->KbdAttr.InputDataQueueLength == 0)
pCfg->KbdAttr.InputDataQueueLength = defaultDataQueueSize;
pCfg->KbdAttr.InputDataQueueLength *= sizeof(KEYBOARD_INPUT_DATA);
if (pCfg->MouAttr.InputDataQueueLength == 0)
pCfg->MouAttr.InputDataQueueLength = defaultDataQueueSize;
pCfg->MouAttr.InputDataQueueLength *= sizeof(MOUSE_INPUT_DATA);
pCfg->MouAttr.NumberOfButtons = (USHORT)cButtons;
pCfg->MouAttr.SampleRate = (USHORT)sampleRate;
pCfg->MouseResolution = (USHORT)mouseResolution;
if (overrideKeyboardType != invalidKeyboardType)
{
if (overrideKeyboardType <= RT_ELEMENTS(s_aKeybType))
pCfg->KbdAttr.KeyboardIdentifier.Type = (UCHAR) overrideKeyboardType;
}
if (overrideKeyboardSubtype != invalidKeyboardSubtype)
pCfg->KbdAttr.KeyboardIdentifier.Subtype = (UCHAR) overrideKeyboardSubtype;
if (pInit->DevExt.MouExt.SynchTickCount == 0)
pInit->DevExt.MouExt.SynchTickCount = defaultSynchPacket100ns;
pInit->DevExt.MouExt.SynchTickCount /= KeQueryTimeIncrement();
if (parametersPath.Buffer)
ExFreePool(parametersPath.Buffer);
if (aQuery)
ExFreePool(aQuery);
}
static void GetDevIdentifier(PKEY_VALUE_FULL_INFORMATION *ppInf, PUNICODE_STRING pStr)
{
pStr->Length = (USHORT)(*(ppInf + IoQueryDeviceIdentifier))->DataLength;
if (!pStr->Length)
return;
pStr->MaximumLength = pStr->Length;
pStr->Buffer = (PWSTR) (((PUCHAR)(*(ppInf + IoQueryDeviceIdentifier)))
+ (*(ppInf + IoQueryDeviceIdentifier))->DataOffset);
}
static ULONG GetDevCfgData(PKEY_VALUE_FULL_INFORMATION *ppInf, PCM_PARTIAL_RESOURCE_LIST *ppData)
{
ULONG DataLength = (*(ppInf + IoQueryDeviceConfigurationData))->DataLength;
if (DataLength)
*ppData = (PCM_PARTIAL_RESOURCE_LIST)( ((PUCHAR) (*(ppInf + IoQueryDeviceConfigurationData)))
+ (*(ppInf + IoQueryDeviceConfigurationData))->DataOffset
+ FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList));
return DataLength;
}
/**
* Callout routine. Grab keyboard controller and peripheral configuration
* information.
*/
static NTSTATUS KbdCallOut(PVOID pCtx, PUNICODE_STRING PathName,
INTERFACE_TYPE BusType, ULONG uBusNr, PKEY_VALUE_FULL_INFORMATION *pBusInf,
CONFIGURATION_TYPE uCtrlType, ULONG uCtrlNr, PKEY_VALUE_FULL_INFORMATION *pCtrlInf,
CONFIGURATION_TYPE uPrfType, ULONG uPrfNr, PKEY_VALUE_FULL_INFORMATION *pPrfInf)
{
UNICODE_STRING unicodeIdentifier;
GetDevIdentifier(pPrfInf, &unicodeIdentifier);
PINITEXT pInit = (PINITEXT)pCtx;
PDEVEXT pDevExt = &pInit->DevExt;
if ( (pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
|| !unicodeIdentifier.Length)
return STATUS_SUCCESS;
pDevExt->HardwarePresent |= KEYBOARD_HARDWARE_PRESENT;
PI8042CFGINF pCfg = &pDevExt->Cfg;
pCfg->KbdAttr.KeyboardIdentifier.Type = 0;
pCfg->KbdAttr.KeyboardIdentifier.Subtype = 0;
PCM_PARTIAL_RESOURCE_LIST pPrfData;
if (GetDevCfgData(pPrfInf, &pPrfData))
{
unsigned cList = pPrfData->Count;
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDesc = pPrfData->PartialDescriptors;
for (unsigned i = 0; i < cList; i++, pResDesc++)
{
switch (pResDesc->Type)
{
case CmResourceTypeDeviceSpecific:
{
PCM_KEYBOARD_DEVICE_DATA KbdData = (PCM_KEYBOARD_DEVICE_DATA)(((PUCHAR)pResDesc)
+ sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
if (KbdData->Type <= RT_ELEMENTS(s_aKeybType))
pCfg->KbdAttr.KeyboardIdentifier.Type = KbdData->Type;
pCfg->KbdAttr.KeyboardIdentifier.Subtype = KbdData->Subtype;
pCfg->KbdInd.LedFlags = (KbdData->KeyboardFlags >> 4) & 7;
break;
}
default:
break;
}
}
}
if (pCfg->KbdAttr.KeyboardIdentifier.Type == 0)
{
pCfg->KbdAttr.KeyboardIdentifier.Type = 4;
pCfg->KbdInd.LedFlags = 0;
}
pCfg->InterfaceType = BusType;
pCfg->uBusNr = uBusNr;
pCfg->fFloatSave = FALSE;
BOOLEAN fDefIntShare;
KINTERRUPT_MODE DefIntMode;
if (BusType == MicroChannel)
{
fDefIntShare = TRUE;
DefIntMode = LevelSensitive;
}
else
{
fDefIntShare = FALSE;
DefIntMode = Latched;
}
PCM_PARTIAL_RESOURCE_LIST pCtrlData;
if (GetDevCfgData(pCtrlInf, &pCtrlData))
{
unsigned cList = pCtrlData->Count;
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDesc = pCtrlData->PartialDescriptors;
for (unsigned i = 0; i < cList; i++, pResDesc++)
{
switch (pResDesc->Type)
{
case CmResourceTypePort:
ASSERT(pCfg->cPorts < i8042MaxPorts);
pCfg->aPorts[pCfg->cPorts] = *pResDesc;
pCfg->aPorts[pCfg->cPorts].ShareDisposition = CmResourceShareDriverExclusive;
pCfg->cPorts++;
break;
case CmResourceTypeInterrupt:
pCfg->KbdInt = *pResDesc;
pCfg->KbdInt.ShareDisposition = fDefIntShare ? CmResourceShareShared
: CmResourceShareDeviceExclusive;
break;
case CmResourceTypeDeviceSpecific:
break;
default:
break;
}
}
}
if (!(pCfg->KbdInt.Type & CmResourceTypeInterrupt))
{
pCfg->KbdInt.Type = CmResourceTypeInterrupt;
pCfg->KbdInt.ShareDisposition = fDefIntShare ? CmResourceShareShared
: CmResourceShareDeviceExclusive;
pCfg->KbdInt.Flags = (DefIntMode == Latched) ? CM_RESOURCE_INTERRUPT_LATCHED
: CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
pCfg->KbdInt.u.Interrupt.Level = 1;
pCfg->KbdInt.u.Interrupt.Vector = 1;
}
if (pCfg->cPorts == 0)
{
pCfg->aPorts[i8042Dat].Type = CmResourceTypePort;
pCfg->aPorts[i8042Dat].Flags = CM_RESOURCE_PORT_IO;
pCfg->aPorts[i8042Dat].ShareDisposition = CmResourceShareDriverExclusive;
pCfg->aPorts[i8042Dat].u.Port.Start.LowPart = 0x60;
pCfg->aPorts[i8042Dat].u.Port.Start.HighPart = 0;
pCfg->aPorts[i8042Dat].u.Port.Length = 1;
pCfg->aPorts[i8042Cmd].Type = CmResourceTypePort;
pCfg->aPorts[i8042Cmd].Flags = CM_RESOURCE_PORT_IO;
pCfg->aPorts[i8042Cmd].ShareDisposition = CmResourceShareDriverExclusive;
pCfg->aPorts[i8042Cmd].u.Port.Start.LowPart = 0x64;
pCfg->aPorts[i8042Cmd].u.Port.Start.HighPart = 0;
pCfg->aPorts[i8042Cmd].u.Port.Length = 1;
pCfg->cPorts = 2;
}
else if (pCfg->cPorts == 1)
{
pCfg->aPorts[i8042Dat].u.Port.Length = 1;
pCfg->aPorts[i8042Cmd] = pCfg->aPorts[i8042Dat];
pCfg->aPorts[i8042Cmd].u.Port.Start.LowPart += 4;
pCfg->cPorts++;
}
else
{
if (pCfg->aPorts[i8042Cmd].u.Port.Start.LowPart < pCfg->aPorts[i8042Dat].u.Port.Start.LowPart)
{
CM_PARTIAL_RESOURCE_DESCRIPTOR Desc = pCfg->aPorts[i8042Dat];
pCfg->aPorts[i8042Dat] = pCfg->aPorts[i8042Cmd];
pCfg->aPorts[i8042Cmd] = Desc;
}
}
return STATUS_SUCCESS;
}
/**
* Callout routine. Grab the pointer controller and the peripheral
* configuration information.
*/
static NTSTATUS MouCallOut(PVOID pCtx, PUNICODE_STRING PathName,
INTERFACE_TYPE BusType, ULONG uBusNr, PKEY_VALUE_FULL_INFORMATION *pBusInf,
CONFIGURATION_TYPE uCtrlType, ULONG uCtrlNr, PKEY_VALUE_FULL_INFORMATION *pCtrlInf,
CONFIGURATION_TYPE uPrfType, ULONG uPrfNr, PKEY_VALUE_FULL_INFORMATION *pPrfInf)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING unicodeIdentifier;
GetDevIdentifier(pPrfInf, &unicodeIdentifier);
PINITEXT pInit = (PINITEXT)pCtx;
PDEVEXT pDevExt = &pInit->DevExt;
if ( (pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT)
|| unicodeIdentifier.Length == 0)
return status;
ANSI_STRING ansiString;
status = RtlUnicodeStringToAnsiString(&ansiString, &unicodeIdentifier, TRUE);
if (!NT_SUCCESS(status))
return status;
if (strstr(ansiString.Buffer, "PS2"))
pDevExt->HardwarePresent |= MOUSE_HARDWARE_PRESENT;
RtlFreeAnsiString(&ansiString);
if (!(pDevExt->HardwarePresent & MOUSE_HARDWARE_PRESENT))
return status;
PI8042CFGINF pCfg = &pDevExt->Cfg;
if (!(pDevExt->HardwarePresent & KEYBOARD_HARDWARE_PRESENT))
{
pCfg->InterfaceType = BusType;
pCfg->uBusNr = uBusNr;
pCfg->fFloatSave = FALSE;
}
BOOLEAN fDefIntShare;
KINTERRUPT_MODE DefIntMode;
if (pCfg->InterfaceType == MicroChannel)
{
fDefIntShare = TRUE;
DefIntMode = LevelSensitive;
}
else
{
fDefIntShare = FALSE;
DefIntMode = Latched;
}
PCM_PARTIAL_RESOURCE_LIST pCtrlData;
if (GetDevCfgData(pCtrlInf, &pCtrlData))
{
unsigned cList = pCtrlData->Count;
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDesc = pCtrlData->PartialDescriptors;
BOOLEAN fPortInfoNeeded = pCfg->cPorts ? FALSE : TRUE;
for (unsigned i = 0; i < cList; i++, pResDesc++)
{
switch (pResDesc->Type)
{
case CmResourceTypePort:
if (fPortInfoNeeded)
{
pCfg->aPorts[pCfg->cPorts] = *pResDesc;
pCfg->aPorts[pCfg->cPorts].ShareDisposition = CmResourceShareDriverExclusive;
pCfg->cPorts++;
}
break;
case CmResourceTypeInterrupt:
pCfg->MouInt = *pResDesc;
pCfg->MouInt.ShareDisposition = fDefIntShare ? CmResourceShareShared
: CmResourceShareDeviceExclusive;
break;
default:
break;
}
}
}
if (!(pCfg->MouInt.Type & CmResourceTypeInterrupt))
{
pCfg->MouInt.Type = CmResourceTypeInterrupt;
pCfg->MouInt.ShareDisposition = fDefIntShare ? CmResourceShareShared
: CmResourceShareDeviceExclusive;
pCfg->MouInt.Flags = (DefIntMode == Latched) ? CM_RESOURCE_INTERRUPT_LATCHED
: CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
pCfg->MouInt.u.Interrupt.Level = 12;
pCfg->MouInt.u.Interrupt.Vector = 12;
}
if (pCfg->cPorts == 0)
{
pCfg->aPorts[i8042Dat].Type = CmResourceTypePort;
pCfg->aPorts[i8042Dat].Flags = CM_RESOURCE_PORT_IO;
pCfg->aPorts[i8042Dat].ShareDisposition = CmResourceShareDriverExclusive;
pCfg->aPorts[i8042Dat].u.Port.Start.LowPart = 0x60;
pCfg->aPorts[i8042Dat].u.Port.Start.HighPart = 0;
pCfg->aPorts[i8042Dat].u.Port.Length = 1;
pCfg->aPorts[i8042Cmd].Type = CmResourceTypePort;
pCfg->aPorts[i8042Cmd].Flags = CM_RESOURCE_PORT_IO;
pCfg->aPorts[i8042Cmd].ShareDisposition = CmResourceShareDriverExclusive;
pCfg->aPorts[i8042Cmd].u.Port.Start.LowPart = 0x64;
pCfg->aPorts[i8042Cmd].u.Port.Start.HighPart = 0;
pCfg->aPorts[i8042Cmd].u.Port.Length = 1;
pCfg->cPorts = 2;
}
else if (pCfg->cPorts == 1)
{
pCfg->aPorts[i8042Cmd] = pCfg->aPorts[i8042Dat];
pCfg->aPorts[i8042Cmd].u.Port.Start.LowPart += 4;
pCfg->cPorts++;
}
else
{
if (pCfg->aPorts[i8042Cmd].u.Port.Start.LowPart < pCfg->aPorts[i8042Dat].u.Port.Start.LowPart)
{
CM_PARTIAL_RESOURCE_DESCRIPTOR Desc = pCfg->aPorts[i8042Dat];
pCfg->aPorts[i8042Dat] = pCfg->aPorts[i8042Cmd];
pCfg->aPorts[i8042Cmd] = Desc;
}
}
return status;
}
static const UCHAR s_ucCommands[] =
{
SET_MOUSE_SAMPLING_RATE, 200,
SET_MOUSE_SAMPLING_RATE, 100,
SET_MOUSE_SAMPLING_RATE, 80,
GET_DEVICE_ID, 0
};
static NTSTATUS MouFindWheel(PDEVICE_OBJECT pDevObj)
{
NTSTATUS status;
PDEVEXT pDevExt = (PDEVEXT) pDevObj->DeviceExtension;
if (!pDevExt->Cfg.EnableWheelDetection)
return STATUS_NO_SUCH_DEVICE;
KeStallExecutionProcessor(50);
for (unsigned iCmd = 0; s_ucCommands[iCmd];)
{
status = PutBytePoll(i8042Dat, TRUE /*=wait*/, MouDevType, pDevExt, s_ucCommands[iCmd]);
if (!NT_SUCCESS(status))
goto fail;
iCmd++;
KeStallExecutionProcessor(50);
}
UCHAR byte;
for (unsigned i = 0; i < 5; i++)
{
status = GetBytePoll(CtrlDevType, pDevExt, &byte);
if (status != STATUS_IO_TIMEOUT)
break;
KeStallExecutionProcessor(50);
}
if ( NT_SUCCESS(status)
&& (byte == MOUSE_ID_BYTE || byte == WHEELMOUSE_ID_BYTE))
{
if (byte == WHEELMOUSE_ID_BYTE)
{
pDevExt->HardwarePresent |= (WHEELMOUSE_HARDWARE_PRESENT | MOUSE_HARDWARE_PRESENT);
pDevExt->Cfg.MouAttr.MouseIdentifier = WHEELMOUSE_I8042_HARDWARE;
}
else
pDevExt->HardwarePresent |= MOUSE_HARDWARE_PRESENT;
}
fail:
pDevExt->MouExt.uPrevSignAndOverflow = 0;
pDevExt->MouExt.InputState = MouseExpectingACK;
return status;
}