/* $Id: VBoxPS2NT.cpp 40353 2012-03-05 13:18:23Z vboxsync $ */ /** @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 #include #include #include #include #include RT_C_DECLS_BEGIN #undef PAGE_SIZE #undef PAGE_SHIFT #include #include #include 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, ®istryPath, &KbdNameBase, &MouNameBase); MouGetRegstry(pInit, ®istryPath, &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(¶metersPath, 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(¶metersPath, path); RtlAppendUnicodeToString(¶metersPath, 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; }