diff --git a/reactos/drivers/input/i8042prt/README.txt b/reactos/drivers/input/i8042prt/README.txt new file mode 100644 index 00000000000..a8f09ff5f59 --- /dev/null +++ b/reactos/drivers/input/i8042prt/README.txt @@ -0,0 +1,83 @@ +Intel 8042 port driver + +This directory contains a driver for Intels 8042 and compatible controllers. +It is based on the information in the DDK documentation on MSDN. It is intended +to be compatible with keyboard and mouse drivers written for Windows. It is +not based on the i8042prt example driver that's included with the DDK. + +The directory contains these files: + +i8042prt.c: Main controller functionality, things shared by keyboards and mice + +keyboard.c: keyboard functionality: detection, interrupt handling + +mouse.c: mouse functionality: detection, interrupt handling, packet parsing for + standard ps2 and microsoft mice + +ps2pp.c: logitech ps2++ mouse packat parsing (basic) + +registry.c: registry reading + +makefile, i8042prt.rc: obvious + + +Some parts of the driver make little sense. This is because it implements +an interface that has evolved over a long time, and because the ps/2 +'standard' is really awful. + +Things to add: + +- Better AT (before ps2) keyboard handling +- SiS keyboard controller detection +- Mouse identification +- General robustness: reset mouse if things go wrong +- Handling all registry settings +- ACPI +- Make it work more like a WDM driver + +Things not to add: + +- Other mouse protocols, touchpad handling etc. : Write a filter driver instead +- Keyboard lights handling: Should be in win32k +- Keyboard scancode translation: Should be in win32k + +Things requiring work elsewhere: + +- Debugger interface (TAB + key): + Currently this interface wants translated keycodes, which are not + implemented by this driver. As it just uses a giant switch with + hardcoded cases, this should not be hard to fix. + +- Class drivers: + I wrote a keyboard class driver, which does keycode translations. It + should not do this, win32k should get untranslated keycodes and do + the translation itself. + + I changed the mouse class driver (mouclass) to work like Microsofts mouclass. + Unfortunately this means that the original psaux driver doesn't work + anymore (the same holds for the other mice drivers, probably). + + The keyboard class driver passes on ioctls from win32k, so it can change + keyboard settings. As far as I could see, the mouse class driver does not + do this yet. + + The class drivers should be able to handle reads for more than one packet + at a time (kbdclass should, mouclass does not). Win32k should send such + requests. + + +I put a lot of work in making it work like Microsofts driver does, so third party drivers can work. Please keep it that way. + + +Links: + +Here's a link describing most of the registry settings: + +http://www.microsoft.com/resources/documentation/Windows/2000/server/reskit/en-us/Default.asp?url=/resources/documentation/Windows/2000/server/reskit/en-us/regentry/31493.asp + +PS/2 protocol documentation: + +http://www.win.tue.nl/~aeb/linux/kbd/scancodes.html + +It also contains a link to a description of the ps2++ protocol, which has +since disappeared. Archive.org still has it. diff --git a/reactos/drivers/input/i8042prt/i8042prt.c b/reactos/drivers/input/i8042prt/i8042prt.c new file mode 100644 index 00000000000..04625caa859 --- /dev/null +++ b/reactos/drivers/input/i8042prt/i8042prt.c @@ -0,0 +1,927 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/input/i8042prt/i8042prt.c + * PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver + * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com) + * Jason Filby (jasonfilby@yahoo.com) + * Tinus + */ + +/* INCLUDES ****************************************************************/ + +#define NDEBUG +#include + +#include "i8042prt.h" + +/* GLOBALS *******************************************************************/ + +/* + * Driver data + */ +#define I8042_TIMEOUT 500000 + +#define I8042_MAX_COMMAND_LENGTH 16 +#define I8042_MAX_UPWARDS_STACK 5 + +UNICODE_STRING I8042RegistryPath; + +/* FUNCTIONS *****************************************************************/ + +/* + * FUNCTION: Write data to a port, waiting first for it to become ready + */ +BOOLEAN I8042Write(PDEVICE_EXTENSION DevExt, PUCHAR addr, UCHAR data) +{ + ULONG ResendIterations = DevExt->Settings.PollingIterations; + + while ((KBD_IBF & READ_PORT_UCHAR(I8042_CTRL_PORT)) && + (ResendIterations--)) + { + KeStallExecutionProcessor(50); + } + + if (ResendIterations) { + WRITE_PORT_UCHAR(addr,data); + DPRINT("Sent %x to %x\n", data, addr); + return TRUE; + } + return FALSE; +} + +#if 0 /* function is not needed */ +/* + * FUNCTION: Write data to a port, without waiting first + */ +static BOOLEAN I8042WriteNoWait(PDEVICE_EXTENSION DevExt, int addr, UCHAR data) +{ + WRITE_PORT_UCHAR((PUCHAR)addr,data); + DPRINT("Sent %x to %x\n", data, addr); + return TRUE; +} +#endif + +/* + * FUNCTION: Read data from port 0x60 + */ +NTSTATUS I8042ReadData(UCHAR *Data) +{ + UCHAR Status; + Status=READ_PORT_UCHAR(I8042_CTRL_PORT); + + // If data is available + if ((Status & KBD_OBF)) { + Data[0]=READ_PORT_UCHAR((PUCHAR)I8042_DATA_PORT); + DPRINT("Read: %x (status: %x)\n", Data[0], Status); + + // If the data is valid (not timeout, not parity error) + if (0 == (Status & KBD_PERR)) + return STATUS_SUCCESS; + } + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS I8042ReadStatus(UCHAR *Status) +{ + Status[0]=READ_PORT_UCHAR(I8042_CTRL_PORT); + return STATUS_SUCCESS; +} + +/* + * FUNCTION: Read data from port 0x60 + */ +NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data) +{ + ULONG Counter = DevExt->Settings.PollingIterations; + NTSTATUS Status; + + while (Counter--) { + Status = I8042ReadData(Data); + + if (STATUS_SUCCESS == Status) + return Status; + + KeStallExecutionProcessor(50); + } + // Timed out + return STATUS_IO_TIMEOUT; +} + +VOID I8042Flush() +{ + UCHAR Ignore; + + while (STATUS_SUCCESS == I8042ReadData(&Ignore)) { + DPRINT("Data flushed\n"); /* drop */ + } +} + +VOID STDCALL I8042IsrWritePort(PDEVICE_EXTENSION DevExt, + UCHAR Value, + UCHAR SelectCmd) +{ + if (SelectCmd) + if (!I8042Write(DevExt, I8042_CTRL_PORT, SelectCmd)) + return; + + I8042Write(DevExt, I8042_DATA_PORT, Value); +} + +/* + * These functions are callbacks for filter driver custom + * initialization routines. + */ +NTSTATUS STDCALL I8042SynchWritePort(PDEVICE_EXTENSION DevExt, + UCHAR Port, + UCHAR Value, + BOOLEAN WaitForAck) +{ + NTSTATUS Status; + UCHAR Ack; + ULONG ResendIterations = DevExt->Settings.ResendIterations + 1; + + do { + if (Port) + if (!I8042Write(DevExt, I8042_DATA_PORT, Port)) + { + DPRINT1("Failed to write Port\n"); + return STATUS_IO_TIMEOUT; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) + { + DPRINT1("Failed to write Value\n"); + return STATUS_IO_TIMEOUT; + } + + if (WaitForAck) { + Status = I8042ReadDataWait(DevExt, &Ack); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to read Ack\n"); + return Status; + } + if (Ack == KBD_ACK) + return STATUS_SUCCESS; + if (Ack != KBD_RESEND) + { + DPRINT1("Unexpected Ack 0x%x\n", Ack); + return STATUS_UNEXPECTED_IO_ERROR; + } + } else { + return STATUS_SUCCESS; + } + DPRINT("Reiterating\n"); + ResendIterations--; + } while (ResendIterations); + return STATUS_IO_TIMEOUT; +} + +/* + * This one reads a value from the port; You don't have to specify + * which one, it'll always be from the one you talked to, so one function + * is enough this time. Note how MSDN specifies the + * WaitForAck parameter to be ignored. + */ +static NTSTATUS STDCALL I8042SynchReadPort(PVOID Context, + PUCHAR Value, + BOOLEAN WaitForAck) +{ + PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)Context; + + return I8042ReadDataWait(DevExt, Value); +} + +/* Write the current byte of the packet. Returns FALSE in case + * of problems. + */ +static BOOLEAN STDCALL I8042PacketWrite(PDEVICE_EXTENSION DevExt) +{ + UCHAR Port = DevExt->PacketPort; + + if (Port) { + if (!I8042Write(DevExt, + I8042_CTRL_PORT, + Port)) { + /* something is really wrong! */ + DPRINT1("Failed to send packet byte!\n"); + return FALSE; + } + } + + return I8042Write(DevExt, + I8042_DATA_PORT, + DevExt->Packet.Bytes[DevExt->Packet.CurrentByte]); +} + + +/* + * This function starts a packet. It must be called with the + * correct DIRQL. + */ +NTSTATUS STDCALL I8042StartPacket(PDEVICE_EXTENSION DevExt, + PDEVICE_OBJECT Device, + PUCHAR Bytes, + ULONG ByteCount, + PIRP Irp) +{ + KIRQL Irql; + NTSTATUS Status; + PFDO_DEVICE_EXTENSION FdoDevExt = Device->DeviceExtension; + + Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + + DevExt->CurrentIrp = Irp; + DevExt->CurrentIrpDevice = Device; + + if (Idle != DevExt->Packet.State) { + Status = STATUS_DEVICE_BUSY; + goto startpacketdone; + } + + DevExt->Packet.Bytes = Bytes; + DevExt->Packet.CurrentByte = 0; + DevExt->Packet.ByteCount = ByteCount; + DevExt->Packet.State = SendingBytes; + DevExt->PacketResult = Status = STATUS_PENDING; + + if (Mouse == FdoDevExt->Type) + DevExt->PacketPort = 0xD4; + else + DevExt->PacketPort = 0; + + if (!I8042PacketWrite(DevExt)) { + Status = STATUS_IO_TIMEOUT; + DevExt->Packet.State = Idle; + DevExt->PacketResult = STATUS_ABANDONED; + goto startpacketdone; + } + + DevExt->Packet.CurrentByte++; + +startpacketdone: + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); + + if (STATUS_PENDING != Status) { + DevExt->CurrentIrp = NULL; + DevExt->CurrentIrpDevice = NULL; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + return Status; +} + +BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt, + UCHAR Output) +{ + if (Idle == DevExt->Packet.State) + return FALSE; + + switch (Output) { + case KBD_RESEND: + DevExt->PacketResends++; + if (DevExt->PacketResends > DevExt->Settings.ResendIterations) { + DevExt->Packet.State = Idle; + DevExt->PacketComplete = TRUE; + DevExt->PacketResult = STATUS_IO_TIMEOUT; + DevExt->PacketResends = 0; + return TRUE; + } + DevExt->Packet.CurrentByte--; + break; + + case KBD_NACK: + DevExt->Packet.State = Idle; + DevExt->PacketComplete = TRUE; + DevExt->PacketResult = STATUS_UNEXPECTED_IO_ERROR; + DevExt->PacketResends = 0; + return TRUE; + + default: + DevExt->PacketResends = 0; + } + + if (DevExt->Packet.CurrentByte >= DevExt->Packet.ByteCount) { + DevExt->Packet.State = Idle; + DevExt->PacketComplete = TRUE; + DevExt->PacketResult = STATUS_SUCCESS; + return TRUE; + } + + if (!I8042PacketWrite(DevExt)) { + DevExt->Packet.State = Idle; + DevExt->PacketComplete = TRUE; + DevExt->PacketResult = STATUS_IO_TIMEOUT; + return TRUE; + } + DevExt->Packet.CurrentByte++; + + return TRUE; +} + +VOID I8042PacketDpc(PDEVICE_EXTENSION DevExt) +{ + BOOLEAN FinishIrp = FALSE; + NTSTATUS Result = STATUS_INTERNAL_ERROR; /* Shouldn't happen */ + KIRQL Irql; + + /* If the interrupt happens before this is setup, the key + * was already in the buffer. Too bad! */ + if (!DevExt->HighestDIRQLInterrupt) + return; + + Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + + if (Idle == DevExt->Packet.State && + DevExt->PacketComplete) { + FinishIrp = TRUE; + Result = DevExt->PacketResult; + DevExt->PacketComplete = FALSE; + } + + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, + Irql); + + if (!FinishIrp) + return; + + if (DevExt->CurrentIrp) { + DevExt->CurrentIrp->IoStatus.Status = Result; + IoCompleteRequest(DevExt->CurrentIrp, IO_NO_INCREMENT); + IoStartNextPacket(DevExt->CurrentIrpDevice, FALSE); + DevExt->CurrentIrp = NULL; + DevExt->CurrentIrpDevice = NULL; + } +} + +VOID STDCALL I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject, + PVOID Context) +{ + KEVENT Event; + IO_STATUS_BLOCK IoStatus; + NTSTATUS Status; + PDEVICE_EXTENSION DevExt; + PFDO_DEVICE_EXTENSION FdoDevExt; + PIRP NewIrp; + PI8042_HOOK_WORKITEM WorkItemData = (PI8042_HOOK_WORKITEM)Context; + + ULONG IoControlCode; + PVOID InputBuffer; + ULONG InputBufferLength; + BOOLEAN IsKbd; + + DPRINT("HookWorkItem\n"); + + FdoDevExt = (PFDO_DEVICE_EXTENSION) + DeviceObject->DeviceExtension; + + DevExt = FdoDevExt->PortDevExt; + + if (WorkItemData->Target == DevExt->KeyboardData.ClassDeviceObject) { + IoControlCode = IOCTL_INTERNAL_I8042_HOOK_KEYBOARD; + InputBuffer = &DevExt->KeyboardHook; + InputBufferLength = sizeof(INTERNAL_I8042_HOOK_KEYBOARD); + IsKbd = TRUE; + DPRINT ("is for keyboard.\n"); + } else if (WorkItemData->Target == DevExt->MouseData.ClassDeviceObject){ + IoControlCode = IOCTL_INTERNAL_I8042_HOOK_MOUSE; + InputBuffer = &DevExt->MouseHook; + InputBufferLength = sizeof(INTERNAL_I8042_HOOK_MOUSE); + IsKbd = FALSE; + DPRINT ("is for mouse.\n"); + } else { + DPRINT1("I8042SendHookWorkItem: Can't find DeviceObject\n"); + WorkItemData->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR; + goto hookworkitemdone; + } + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + NewIrp = IoBuildDeviceIoControlRequest( + IoControlCode, + WorkItemData->Target, + InputBuffer, + InputBufferLength, + NULL, + 0, + TRUE, + &Event, + &IoStatus); + + if (!NewIrp) { + DPRINT("IOCTL_INTERNAL_(device)_CONNECT: " + "Can't allocate IRP\n"); + WorkItemData->Irp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + goto hookworkitemdone; + } + +#if 0 + Status = IoCallDriver( + WorkItemData->Target, + NewIrp); + + if (STATUS_PENDING == Status) + KeWaitForSingleObject(&Event, + Executive, + KernelMode, + FALSE, + NULL); +#endif + + if (IsKbd) { + /* Call the hooked initialization if it exists */ + if (DevExt->KeyboardHook.InitializationRoutine) { + Status = DevExt->KeyboardHook.InitializationRoutine( + DevExt->KeyboardHook.Context, + DevExt, + I8042SynchReadPort, + I8042SynchWritePortKbd, + FALSE); + if (!NT_SUCCESS(Status)) { + WorkItemData->Irp->IoStatus.Status = Status; + goto hookworkitemdone; + } + } + /* TODO: Now would be the right time to enable the interrupt */ + + DevExt->KeyboardClaimed = TRUE; + } else { + /* Mouse doesn't have this, but we need to send a + * reset to start the detection. + */ + KIRQL Irql; + + Irql = KeAcquireInterruptSpinLock( + DevExt->HighestDIRQLInterrupt); + + I8042Write(DevExt, I8042_CTRL_PORT, 0xD4); + I8042Write(DevExt, I8042_DATA_PORT, 0xFF); + + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); + } + + WorkItemData->Irp->IoStatus.Status = STATUS_SUCCESS; + +hookworkitemdone: + WorkItemData->Irp->IoStatus.Information = 0; + IoCompleteRequest(WorkItemData->Irp, IO_NO_INCREMENT); + + IoFreeWorkItem(WorkItemData->WorkItem); + ExFreePool(WorkItemData); + DPRINT("HookWorkItem done\n"); +} + +static VOID STDCALL I8042StartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + if (!I8042StartIoKbd(DeviceObject, Irp)) { + DPRINT1("Unhandled StartIo!\n"); + } +} + +static NTSTATUS STDCALL I8042InternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + + DPRINT("InternalDeviceControl\n"); + + switch (FdoDevExt->Type) { + case Keyboard: + Status = I8042InternalDeviceControlKbd(DeviceObject, Irp); + break; + case Mouse: + Status = I8042InternalDeviceControlMouse(DeviceObject, Irp); + break; + } + + if (Status == STATUS_INVALID_DEVICE_REQUEST) { + DPRINT1("Invalid internal device request!\n"); + } + + if (Status != STATUS_PENDING) + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +static NTSTATUS STDCALL I8042CreateDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS Status; + + DPRINT ("I8042CreateDispatch\n"); + + Status = STATUS_SUCCESS; + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +static NTSTATUS STDCALL I8042BasicDetect(PDEVICE_EXTENSION DevExt) +{ + NTSTATUS Status; + UCHAR Value = 0; + ULONG Counter; + + DevExt->MouseExists = FALSE; + DevExt->KeyboardExists = FALSE; + + I8042Flush(); + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST)) { + DPRINT1("Writing KBD_SELF_TEST command failed\n"); + return STATUS_IO_TIMEOUT; + } + + // Wait longer? + Counter = 10; + do { + Status = I8042ReadDataWait(DevExt, &Value); + } while ((Counter--) && (STATUS_IO_TIMEOUT == Status)); + + if (!NT_SUCCESS(Status)) { + DPRINT1("Failed to read KBD_SELF_TEST response, status 0x%x\n", + Status); + return Status; + } + + if (Value != 0x55) { + DPRINT1("Got %x instead of 55\n", Value); + return STATUS_IO_DEVICE_ERROR; + } + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value |= CCB_KBD_DISAB | CCB_MOUSE_DISAB; /* disable keyboard/mouse */ + Value &= ~(CCB_KBD_INT_ENAB | CCB_MOUSE_INT_ENAB); + /* don't enable keyboard and mouse interrupts */ + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + /* + * We used to send a KBD_LINE_TEST command here, but on at least HP + * Pavilion notebooks the response to that command was incorrect. + * So now we just assume that a keyboard is attached. + */ + DevExt->KeyboardExists = TRUE; + + if (I8042Write(DevExt, I8042_CTRL_PORT, MOUSE_LINE_TEST)) + { + Status = I8042ReadDataWait(DevExt, &Value); + if (NT_SUCCESS(Status) && Value == 0) + DevExt->MouseExists = TRUE; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS STDCALL I8042Initialize(PDEVICE_EXTENSION DevExt) +{ + NTSTATUS Status; + + Status = I8042BasicDetect(DevExt); + if (!NT_SUCCESS(Status)) { + DPRINT1("Basic keyboard detection failed: %x\n", Status); + return Status; + } + + if (DevExt->MouseExists) { + DPRINT("Aux port detected\n"); + DevExt->MouseExists = I8042DetectMouse(DevExt); + } + + if (!DevExt->KeyboardExists) { + DPRINT("Keyboard port not detected\n"); + if (DevExt->Settings.Headless) + /* Act as if it exists regardless */ + DevExt->KeyboardExists = TRUE; + } else { + DPRINT("Keyboard port detected\n"); + DevExt->KeyboardExists = I8042DetectKeyboard(DevExt); + } + + if (DevExt->KeyboardExists) { + DPRINT("Keyboard detected\n"); + I8042KeyboardEnable(DevExt); + I8042KeyboardEnableInterrupt(DevExt); + } + + if (DevExt->MouseExists) { + DPRINT("Mouse detected\n"); + I8042MouseEnable(DevExt); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS +AddRegistryEntry( + IN PCWSTR PortTypeName, + IN PUNICODE_STRING DeviceName, + IN PCWSTR RegistryPath) +{ + UNICODE_STRING PathU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP"); + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE hDeviceMapKey = (HANDLE)-1; + HANDLE hPortKey = (HANDLE)-1; + UNICODE_STRING PortTypeNameU; + NTSTATUS Status; + + InitializeObjectAttributes(&ObjectAttributes, &PathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status); + goto cleanup; + } + + RtlInitUnicodeString(&PortTypeNameU, PortTypeName); + InitializeObjectAttributes(&ObjectAttributes, &PortTypeNameU, OBJ_KERNEL_HANDLE, hDeviceMapKey, NULL); + Status = ZwCreateKey(&hPortKey, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status); + goto cleanup; + } + + Status = ZwSetValueKey(hPortKey, DeviceName, 0, REG_SZ, (PVOID)RegistryPath, wcslen(RegistryPath) * sizeof(WCHAR) + sizeof(UNICODE_NULL)); + if (!NT_SUCCESS(Status)) + { + DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status); + goto cleanup; + } + + Status = STATUS_SUCCESS; + +cleanup: + if (hDeviceMapKey != (HANDLE)-1) + ZwClose(hDeviceMapKey); + if (hPortKey != (HANDLE)-1) + ZwClose(hPortKey); + return Status; +} + +static NTSTATUS STDCALL I8042AddDevice(PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT Pdo) +{ + UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardPort8042"); + UNICODE_STRING MouseName = RTL_CONSTANT_STRING(L"\\Device\\PointerPort8042"); + ULONG MappedIrqKeyboard = 0, MappedIrqMouse = 0; + KIRQL DirqlKeyboard = 0; + KIRQL DirqlMouse = 0; + KIRQL DirqlMax; + KAFFINITY Affinity; + NTSTATUS Status; + PDEVICE_EXTENSION DevExt; + PFDO_DEVICE_EXTENSION FdoDevExt; + PDEVICE_OBJECT Fdo; + + DPRINT("I8042AddDevice\n"); + + if (Pdo != NULL) + { + /* Device detected by pnpmgr. Ignore it, as we already have + * detected the keyboard and mouse at first call */ + return STATUS_UNSUCCESSFUL; + } + + Status = IoCreateDevice(DriverObject, + sizeof(DEVICE_EXTENSION), + NULL, + FILE_DEVICE_8042_PORT, + FILE_DEVICE_SECURE_OPEN, + TRUE, + &Fdo); + + if (!NT_SUCCESS(Status)) + return Status; + + DevExt = Fdo->DeviceExtension; + + RtlZeroMemory(DevExt, sizeof(DEVICE_EXTENSION)); + + I8042ReadRegistry(DriverObject, DevExt); + + KeInitializeSpinLock(&DevExt->SpinLock); + InitializeListHead(&DevExt->BusDevices); + + KeInitializeDpc(&DevExt->DpcKbd, + I8042DpcRoutineKbd, + DevExt); + + KeInitializeDpc(&DevExt->DpcMouse, + I8042DpcRoutineMouse, + DevExt); + + KeInitializeDpc(&DevExt->DpcMouseTimeout, + I8042DpcRoutineMouseTimeout, + DevExt); + + KeInitializeTimer(&DevExt->TimerMouseTimeout); + + Status = I8042Initialize(DevExt); + Fdo->Flags &= ~DO_DEVICE_INITIALIZING; + if (!NT_SUCCESS(STATUS_SUCCESS)) { + DPRINT1("Initialization failure: %x\n", Status); + return Status; + } + + if (DevExt->KeyboardExists) { + MappedIrqKeyboard = HalGetInterruptVector(Internal, + 0, + 0, + KEYBOARD_IRQ, + &DirqlKeyboard, + &Affinity); + + Status = IoCreateDevice(DriverObject, + sizeof(FDO_DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_8042_PORT, + FILE_DEVICE_SECURE_OPEN, + TRUE, + &Fdo); + + if (NT_SUCCESS(Status)) + { + AddRegistryEntry(L"KeyboardPort", &DeviceName, L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\i8042prt"); + FdoDevExt = Fdo->DeviceExtension; + + RtlZeroMemory(FdoDevExt, sizeof(FDO_DEVICE_EXTENSION)); + + FdoDevExt->PortDevExt = DevExt; + FdoDevExt->Type = Keyboard; + FdoDevExt->DeviceObject = Fdo; + + Fdo->Flags |= DO_BUFFERED_IO; + + DevExt->DebugWorkItem = IoAllocateWorkItem(Fdo); + DevExt->KeyboardObject = Fdo; + + DevExt->KeyboardBuffer = ExAllocatePoolWithTag( + NonPagedPool, + DevExt->KeyboardAttributes.InputDataQueueLength * + sizeof(KEYBOARD_INPUT_DATA), + TAG_I8042); + + if (!DevExt->KeyboardBuffer) { + DPRINT1("No memory for keyboardbuffer\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + InsertTailList(&DevExt->BusDevices, &FdoDevExt->BusDevices); + Fdo->Flags &= ~DO_DEVICE_INITIALIZING; + } + else + DevExt->KeyboardExists = FALSE; + } + + if (DevExt->MouseExists) { + MappedIrqMouse = HalGetInterruptVector(Internal, + 0, + 0, + MOUSE_IRQ, + &DirqlMouse, + &Affinity); + + Status = IoCreateDevice(DriverObject, + sizeof(FDO_DEVICE_EXTENSION), + &MouseName, + FILE_DEVICE_8042_PORT, + FILE_DEVICE_SECURE_OPEN, + TRUE, + &Fdo); + + if (NT_SUCCESS(Status)) + { + AddRegistryEntry(L"PointerPort", &MouseName, L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\i8042prt"); + FdoDevExt = Fdo->DeviceExtension; + + RtlZeroMemory(FdoDevExt, sizeof(FDO_DEVICE_EXTENSION)); + + FdoDevExt->PortDevExt = DevExt; + FdoDevExt->Type = Mouse; + FdoDevExt->DeviceObject = Fdo; + + Fdo->Flags |= DO_BUFFERED_IO; + DevExt->MouseObject = Fdo; + + DevExt->MouseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + DevExt->MouseAttributes.InputDataQueueLength * + sizeof(MOUSE_INPUT_DATA), + TAG_I8042); + + if (!DevExt->MouseBuffer) { + ExFreePoolWithTag(DevExt->KeyboardBuffer, TAG_I8042); + DPRINT1("No memory for mouse buffer\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + InsertTailList(&DevExt->BusDevices, &FdoDevExt->BusDevices); + Fdo->Flags &= ~DO_DEVICE_INITIALIZING; + } + else + DevExt->MouseExists = FALSE; + } + + if (DirqlKeyboard > DirqlMouse) + DirqlMax = DirqlKeyboard; + else + DirqlMax = DirqlMouse; + + if (DevExt->KeyboardExists) { + Status = IoConnectInterrupt(&DevExt->KeyboardInterruptObject, + I8042InterruptServiceKbd, + (PVOID)DevExt, + &DevExt->SpinLock, + MappedIrqKeyboard, + DirqlKeyboard, + DirqlMax, + LevelSensitive, + FALSE, + Affinity, + FALSE); + + DPRINT("Keyboard Irq Status: %x\n", Status); + } + + if (DevExt->MouseExists) { + Status = IoConnectInterrupt(&DevExt->MouseInterruptObject, + I8042InterruptServiceMouse, + (PVOID)DevExt, + &DevExt->SpinLock, + MappedIrqMouse, + DirqlMouse, + DirqlMax, + LevelSensitive, + FALSE, + Affinity, + FALSE); + + DPRINT("Mouse Irq Status: %x\n", Status); + } + + if (DirqlKeyboard > DirqlMouse) + DevExt->HighestDIRQLInterrupt = DevExt->KeyboardInterruptObject; + else + DevExt->HighestDIRQLInterrupt = DevExt->MouseInterruptObject; + + DPRINT("I8042AddDevice done\n"); + + return(STATUS_SUCCESS); +} + +NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath) +/* + * FUNCTION: Module entry point + */ +{ + DPRINT("I8042 Driver 0.0.1\n"); + + I8042RegistryPath.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters"); + I8042RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, + I8042RegistryPath.MaximumLength, + TAG_I8042); + if (I8042RegistryPath.Buffer == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyUnicodeString(&I8042RegistryPath, RegistryPath); + RtlAppendUnicodeToString(&I8042RegistryPath, L"\\Parameters"); + I8042RegistryPath.Buffer[I8042RegistryPath.Length / sizeof(WCHAR)] = 0; + + + + DriverObject->MajorFunction[IRP_MJ_CREATE] = I8042CreateDispatch; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = + I8042InternalDeviceControl; + + DriverObject->DriverStartIo = I8042StartIo; + DriverObject->DriverExtension->AddDevice = I8042AddDevice; + + return(STATUS_SUCCESS); +} diff --git a/reactos/drivers/input/i8042prt/i8042prt.h b/reactos/drivers/input/i8042prt/i8042prt.h new file mode 100644 index 00000000000..9abf7a91883 --- /dev/null +++ b/reactos/drivers/input/i8042prt/i8042prt.h @@ -0,0 +1,378 @@ +#ifndef _I8042DRV_H +#define _I8042DRV_H + +#include +#include +#include + +#ifdef _MSC_VER + #define STDCALL + #define DDKAPI +#endif + +#define KEYBOARD_IRQ 1 +#define MOUSE_IRQ 12 +#define KBD_BUFFER_SIZE 32 + +#define WHEEL_DELTA 120 + +/*----------------------------------------------------- + * DeviceExtension + * --------------------------------------------------*/ +typedef struct _COMMAND_CONTEXT +{ + int NumInput; + int CurInput; + UCHAR * Input; + int NumOutput; + int CurOutput; + UCHAR * Output; + NTSTATUS Status; + + BOOLEAN GotAck; + KEVENT Event; + + PVOID DevExt; +} COMMAND_CONTEXT, *PCOMMAND_CONTEXT; + +typedef enum _MOUSE_TIMEOUT_STATE +{ + NoChange, + TimeoutStart, + TimeoutCancel +} MOUSE_TIMEOUT_STATE, *PMOUSE_TIMEOUT_STATE; + +/* TODO: part of this should be in the _ATTRIBUTES structs instead */ +typedef struct _I8042_SETTINGS +{ + ULONG Headless; /* done */ + ULONG CrashScroll; + ULONG CrashSysRq; /* done */ + ULONG ReportResetErrors; + ULONG PollStatusIterations; /* done */ + ULONG ResendIterations; /* done */ + ULONG PollingIterations; + ULONG PollingIterationsMaximum; + ULONG OverrideKeyboardType; + ULONG OverrideKeyboardSubtype; + ULONG MouseResendStallTime; + ULONG MouseSynchIn100ns; /* done */ + ULONG MouseResolution; /* done */ + ULONG NumberOfButtons; + ULONG EnableWheelDetection; +} I8042_SETTINGS, *PI8042_SETTINGS; + +typedef enum _I8042_MOUSE_TYPE +{ + GenericPS2, + Intellimouse, + IntellimouseExplorer, + Ps2pp +} I8042_MOUSE_TYPE, *PI8042_MOUSE_TYPE; + +typedef enum _I8042_DEVICE_TYPE +{ + Keyboard, + Mouse +} I8042_DEVICE_TYPE, *PI8042_DEVICE_TYPE; + +typedef struct _I8042_DEVICE +{ + LIST_ENTRY ListEntry; + PDEVICE_OBJECT Pdo; +} I8042_DEVICE, *PI8042_DEVICE; + +typedef struct _DEVICE_EXTENSION +{ + PDEVICE_OBJECT KeyboardObject; + PDEVICE_OBJECT MouseObject; + + CONNECT_DATA KeyboardData; + CONNECT_DATA MouseData; + + BOOLEAN KeyboardExists; + BOOLEAN KeyboardIsAT; + BOOLEAN MouseExists; + + BOOLEAN KeyboardClaimed; + BOOLEAN MouseClaimed; + + ULONG BusNumber; + LIST_ENTRY BusDevices; + + INTERNAL_I8042_START_INFORMATION KeyboardStartInformation; + INTERNAL_I8042_START_INFORMATION MouseStartInformation; + + INTERNAL_I8042_HOOK_KEYBOARD KeyboardHook; + INTERNAL_I8042_HOOK_MOUSE MouseHook; + + PKINTERRUPT KeyboardInterruptObject; + PKINTERRUPT MouseInterruptObject; + PKINTERRUPT HighestDIRQLInterrupt; + KSPIN_LOCK SpinLock; + KDPC DpcKbd; + KDPC DpcMouse; + + KTIMER TimerMouseTimeout; + KDPC DpcMouseTimeout; + MOUSE_TIMEOUT_STATE MouseTimeoutState; + BOOLEAN MouseTimeoutActive; + + KEYBOARD_ATTRIBUTES KeyboardAttributes; + KEYBOARD_INDICATOR_PARAMETERS KeyboardIndicators; + KEYBOARD_TYPEMATIC_PARAMETERS KeyboardTypematic; + + BOOLEAN WantAck; + BOOLEAN WantOutput; + BOOLEAN SignalEvent; + + KEYBOARD_SCAN_STATE KeyboardScanState; + BOOLEAN KeyComplete; + KEYBOARD_INPUT_DATA *KeyboardBuffer; + ULONG KeysInBuffer; + + MOUSE_ATTRIBUTES MouseAttributes; + + MOUSE_STATE MouseState; + BOOLEAN MouseComplete; + MOUSE_RESET_SUBSTATE MouseResetState; + MOUSE_INPUT_DATA *MouseBuffer; + ULONG MouseInBuffer; + USHORT MouseButtonState; + ULARGE_INTEGER MousePacketStartTime; + + UCHAR MouseLogiBuffer[3]; + UCHAR MouseLogitechID; + I8042_MOUSE_TYPE MouseType; + + OUTPUT_PACKET Packet; + ULONG PacketResends; + BOOLEAN PacketComplete; + NTSTATUS PacketResult; + UCHAR PacketBuffer[16]; + UCHAR PacketPort; + + PIRP CurrentIrp; + PDEVICE_OBJECT CurrentIrpDevice; + + /* registry config values */ + I8042_SETTINGS Settings; + + /* Debugger stuff */ + BOOLEAN TabPressed; + ULONG DebugKey; + PIO_WORKITEM DebugWorkItem; +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +typedef struct _FDO_DEVICE_EXTENSION +{ + PDEVICE_EXTENSION PortDevExt; + I8042_DEVICE_TYPE Type; + PDEVICE_OBJECT DeviceObject; + + LIST_ENTRY BusDevices; +} FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION; + +typedef struct _I8042_HOOK_WORKITEM +{ + PIO_WORKITEM WorkItem; + PDEVICE_OBJECT Target; + PIRP Irp; +} I8042_HOOK_WORKITEM, *PI8042_HOOK_WORKITEM; + +/* + * Some defines + */ +#define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24)) +#define TAG_I8042 TAG('8', '0', '4', '2') + +#define KBD_WRAP_MASK 0x1F + +#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) +#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) + + +/* + * Keyboard controller ports + */ + +#define I8042_DATA_PORT ((PUCHAR)0x60) +#define I8042_CTRL_PORT ((PUCHAR)0x64) + + +/* + * Controller commands + */ + +#define KBD_READ_MODE 0x20 +#define KBD_WRITE_MODE 0x60 +#define KBD_SELF_TEST 0xAA +#define KBD_LINE_TEST 0xAB +#define KBD_CTRL_ENABLE 0xAE + +#define MOUSE_LINE_TEST 0xA9 +#define MOUSE_CTRL_ENABLE 0xA8 + +#define KBD_READ_OUTPUT_PORT 0xD0 +#define KBD_WRITE_OUTPUT_PORT 0xD1 + +/* + * Keyboard commands + */ + +#define KBD_SET_LEDS 0xED +#define KBD_GET_ID 0xF2 +#define KBD_ENABLE 0xF4 +#define KBD_DISABLE 0xF5 +#define KBD_RESET 0xFF + + +/* + * Keyboard responces + */ + +#define KBD_BATCC 0xAA +#define KBD_ACK 0xFA +#define KBD_NACK 0xFC +#define KBD_RESEND 0xFE + +/* + * Controller status register bits + */ + +#define KBD_OBF 0x01 +#define KBD_IBF 0x02 +#define KBD_AUX 0x10 +#define KBD_GTO 0x40 +#define KBD_PERR 0x80 + +/* + * Controller command byte bits + */ +#define CCB_KBD_INT_ENAB 0x01 +#define CCB_MOUSE_INT_ENAB 0x02 +#define CCB_SYSTEM_FLAG 0x04 +#define CCB_IGN_KEY_LOCK 0x08 +#define CCB_KBD_DISAB 0x10 +#define CCB_MOUSE_DISAB 0x20 +#define CCB_TRANSLATE 0x40 + + +/* + * LED bits + */ + +#define KBD_LED_SCROLL 0x01 +#define KBD_LED_NUM 0x02 +#define KBD_LED_CAPS 0x04 + +/* + * Mouse responses + */ +#define MOUSE_ACK 0xFA +#define MOUSE_ERROR 0xFC +#define MOUSE_NACK 0xFE + +/* i8042prt.c */ +extern UNICODE_STRING I8042RegistryPath; + +NTSTATUS I8042ReadData(UCHAR *Data); + +NTSTATUS I8042ReadStatus(UCHAR *Status); + +NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data); + +VOID I8042Flush(); + +VOID STDCALL I8042IsrWritePort(PDEVICE_EXTENSION DevExt, + UCHAR Value, + UCHAR SelectCmd); + +NTSTATUS STDCALL I8042SynchWritePort(PDEVICE_EXTENSION DevExt, + UCHAR Port, + UCHAR Value, + BOOLEAN WaitForAck); + +NTSTATUS STDCALL I8042StartPacket(PDEVICE_EXTENSION DevExt, + PDEVICE_OBJECT Device, + PUCHAR Bytes, + ULONG ByteCount, + PIRP Irp); + +BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt, + UCHAR Output); + +VOID I8042PacketDpc(PDEVICE_EXTENSION DevExt); + +VOID STDCALL I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject, + PVOID Context); + +BOOLEAN I8042Write(PDEVICE_EXTENSION DevExt, PUCHAR addr, UCHAR data); + +NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath); + +/* keyboard.c */ +VOID STDCALL I8042IsrWritePortKbd(PVOID Context, + UCHAR Value); + +NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context, + UCHAR Value, + BOOLEAN WaitForAck); + +BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt, + VOID * Context); + +VOID STDCALL I8042DpcRoutineKbd(PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + +BOOLEAN STDCALL I8042StartIoKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp); + +NTSTATUS STDCALL I8042InternalDeviceControlKbd(PDEVICE_OBJECT DeviceObject, + PIRP Irp); + +BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt); + +BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt); + +BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt); + +/* registry.c */ +VOID STDCALL I8042ReadRegistry(PDRIVER_OBJECT DriverObject, + PDEVICE_EXTENSION DevExt); + +/* mouse.c */ +VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + +VOID STDCALL I8042DpcRoutineMouseTimeout(PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + +BOOLEAN STDCALL I8042InterruptServiceMouse(struct _KINTERRUPT *Interrupt, + VOID *Context); + +NTSTATUS STDCALL I8042InternalDeviceControlMouse(PDEVICE_OBJECT DeviceObject, + PIRP Irp); + +VOID STDCALL I8042QueueMousePacket(PVOID Context); + +VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt, + USHORT Mask); + +VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt, + UCHAR Output); + +BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt); +BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt); +BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt); + +/* ps2pp.c */ +VOID I8042MouseHandlePs2pp(PDEVICE_EXTENSION DevExt, UCHAR Input); + +#endif // _KEYBOARD_H_ diff --git a/reactos/drivers/input/i8042prt/i8042prt.rbuild b/reactos/drivers/input/i8042prt/i8042prt.rbuild new file mode 100644 index 00000000000..ca4085c16de --- /dev/null +++ b/reactos/drivers/input/i8042prt/i8042prt.rbuild @@ -0,0 +1,14 @@ + + + . + + + ntoskrnl + hal + i8042prt.c + keyboard.c + mouse.c + ps2pp.c + registry.c + i8042prt.rc + diff --git a/reactos/drivers/input/i8042prt/i8042prt.rc b/reactos/drivers/input/i8042prt/i8042prt.rc new file mode 100644 index 00000000000..6c44176265e --- /dev/null +++ b/reactos/drivers/input/i8042prt/i8042prt.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "I8042 Port Device Driver\0" +#define REACTOS_STR_INTERNAL_NAME "i8042prt\0" +#define REACTOS_STR_ORIGINAL_FILENAME "i8042prt.sys\0" +#include diff --git a/reactos/drivers/input/i8042prt/keyboard.c b/reactos/drivers/input/i8042prt/keyboard.c new file mode 100644 index 00000000000..a301d2ee129 --- /dev/null +++ b/reactos/drivers/input/i8042prt/keyboard.c @@ -0,0 +1,776 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/input/i8042prt/keyboard.c + * PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver + * keyboard specifics + * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com) + * Jason Filby (jasonfilby@yahoo.com) + * Tinus + */ + +/* INCLUDES ****************************************************************/ + +#include "i8042prt.h" + +#define NDEBUG +#include + +/* GLOBALS *******************************************************************/ + +static UCHAR TypematicTable[] = { + 0x00, 0x00, 0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x10, 0x12, /* 0-9 */ + 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1A, /* 10-19 */ + 0x1B, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E }; + +typedef struct _LOCAL_KEYBOARD_INDICATOR_TRANSLATION { + USHORT NumberOfIndicatorKeys; + INDICATOR_LIST IndicatorList[3]; +} LOCAL_KEYBOARD_INDICATOR_TRANSLATION, *PLOCAL_KEYBOARD_INDICATOR_TRANSLATION; + +static LOCAL_KEYBOARD_INDICATOR_TRANSLATION IndicatorTranslation = { 3, { + {0x3A, KEYBOARD_CAPS_LOCK_ON}, + {0x45, KEYBOARD_NUM_LOCK_ON}, + {0x46, KEYBOARD_SCROLL_LOCK_ON}}}; + +static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject, + PVOID Context); + +/* FUNCTIONS *****************************************************************/ + +/* + * These functions are callbacks for filter driver custom interrupt + * service routines. + */ +VOID STDCALL I8042IsrWritePortKbd(PVOID Context, + UCHAR Value) +{ + I8042IsrWritePort(Context, Value, 0); +} + +static VOID STDCALL I8042QueueKeyboardPacket(PVOID Context) +{ + PDEVICE_OBJECT DeviceObject = Context; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + + DevExt->KeyComplete = TRUE; + DevExt->KeysInBuffer++; + if (DevExt->KeysInBuffer > + DevExt->KeyboardAttributes.InputDataQueueLength) { + DPRINT1("Keyboard buffer overflow\n"); + DevExt->KeysInBuffer--; + } + + DPRINT("Irq completes key\n"); + KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL); +} + +/* + * These functions are callbacks for filter driver custom + * initialization routines. + */ +NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context, + UCHAR Value, + BOOLEAN WaitForAck) +{ + return I8042SynchWritePort((PDEVICE_EXTENSION)Context, + 0, + Value, + WaitForAck); +} + +BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt, + VOID * Context) +{ + UCHAR Output; + UCHAR PortStatus; + NTSTATUS Status; + PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context; + BOOLEAN HookContinue = FALSE, HookReturn; + ULONG Iterations = 0; + + KEYBOARD_INPUT_DATA *InputData = + DevExt->KeyboardBuffer + DevExt->KeysInBuffer; + + do { + Status = I8042ReadStatus(&PortStatus); + DPRINT("PortStatus: %x\n", PortStatus); + Status = I8042ReadData(&Output); + Iterations++; + if (STATUS_SUCCESS == Status) + break; + KeStallExecutionProcessor(1); + } while (Iterations < DevExt->Settings.PollStatusIterations); + + if (STATUS_SUCCESS != Status) { + DPRINT("Spurious I8042 interrupt\n"); + return FALSE; + } + + DPRINT("Got: %x\n", Output); + + if (DevExt->KeyboardHook.IsrRoutine) { + HookReturn = DevExt->KeyboardHook.IsrRoutine( + DevExt->KeyboardHook.Context, + InputData, + &DevExt->Packet, + PortStatus, + &Output, + &HookContinue, + &DevExt->KeyboardScanState); + + if (!HookContinue) + return HookReturn; + } + + if (I8042PacketIsr(DevExt, Output)) { + if (DevExt->PacketComplete) { + DPRINT("Packet complete\n"); + KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL); + } + DPRINT("Irq eaten by packet\n"); + return TRUE; + } + + DPRINT("Irq is keyboard input\n"); + + if (Normal == DevExt->KeyboardScanState) { + switch (Output) { + case 0xe0: + DevExt->KeyboardScanState = GotE0; + return TRUE; + case 0xe1: + DevExt->KeyboardScanState = GotE1; + return TRUE; + default: + ;/* continue */ + } + } + + InputData->Flags = 0; + + switch (DevExt->KeyboardScanState) { + case GotE0: + InputData->Flags |= KEY_E0; + break; + case GotE1: + InputData->Flags |= KEY_E1; + break; + default: + ; + } + DevExt->KeyboardScanState = Normal; + + if (Output & 0x80) + InputData->Flags |= KEY_BREAK; + else + InputData->Flags |= KEY_MAKE; + + InputData->MakeCode = Output & 0x7f; + + I8042QueueKeyboardPacket(DevExt->KeyboardObject); + + return TRUE; +} + +VOID STDCALL I8042DpcRoutineKbd(PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1; + ULONG KeysTransferred = 0; + ULONG KeysInBufferCopy; + KIRQL Irql; + + I8042PacketDpc(DevExt); + + if (!DevExt->KeyComplete) + return; + + /* We got the interrupt as it was being enabled, too bad */ + if (!DevExt->HighestDIRQLInterrupt) + return; + + Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + + DevExt->KeyComplete = FALSE; + KeysInBufferCopy = DevExt->KeysInBuffer; + + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); + + /* Test for TAB (debugging) */ + if (DevExt->Settings.CrashSysRq) { + PKEYBOARD_INPUT_DATA InputData = DevExt->KeyboardBuffer + + KeysInBufferCopy - 1; + if (InputData->MakeCode == 0x0F) { + DPRINT("Tab!\n"); + DevExt->TabPressed = !(InputData->Flags & KEY_BREAK); + } else if (DevExt->TabPressed) { + DPRINT ("Queueing work item %x\n", DevExt->DebugWorkItem); + DevExt->DebugKey = InputData->MakeCode; + DevExt->TabPressed = FALSE; + + IoQueueWorkItem(DevExt->DebugWorkItem, + &(I8042DebugWorkItem), + DelayedWorkQueue, + DevExt); + } + } + + DPRINT ("Send a key\n"); + + if (!DevExt->KeyboardData.ClassService) + return; + + ((PSERVICE_CALLBACK_ROUTINE) DevExt->KeyboardData.ClassService)( + DevExt->KeyboardData.ClassDeviceObject, + DevExt->KeyboardBuffer, + DevExt->KeyboardBuffer + KeysInBufferCopy, + &KeysTransferred); + + Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + DevExt->KeysInBuffer -= KeysTransferred; + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); +} + +/* You have to send the rate/delay in a somewhat awkward format */ +static UCHAR I8042GetTypematicByte(USHORT Rate, USHORT Delay) +{ + UCHAR ret; + + if (Rate < 3) { + ret = 0x0; + } else if (Rate > 26) { + ret = 0x1F; + } else { + ret = TypematicTable[Rate]; + } + + if (Delay < 375) { + ; + } else if (Delay < 625) { + ret |= 0x20; + } else if (Delay < 875) { + ret |= 0x40; + } else { + ret |= 0x60; + } + return ret; +} +/* + * Process the keyboard internal device requests + * returns FALSE if it doesn't understand the + * call so someone else can handle it. + */ +BOOLEAN STDCALL I8042StartIoKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PIO_STACK_LOCATION Stk; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + + Stk = IoGetCurrentIrpStackLocation(Irp); + + switch (Stk->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER: + I8042StartPacket( + DevExt, + DeviceObject, + Stk->Parameters.DeviceIoControl.Type3InputBuffer, + Stk->Parameters.DeviceIoControl.InputBufferLength, + Irp); + break; + + case IOCTL_KEYBOARD_SET_INDICATORS: + DevExt->PacketBuffer[0] = 0xED; + DevExt->PacketBuffer[1] = 0; + if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON) + DevExt->PacketBuffer[1] |= 0x04; + + if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON) + DevExt->PacketBuffer[1] |= 0x02; + + if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON) + DevExt->PacketBuffer[1] |= 0x01; + + I8042StartPacket(DevExt, + DeviceObject, + DevExt->PacketBuffer, + 2, + Irp); + break; + case IOCTL_KEYBOARD_SET_TYPEMATIC: + DevExt->PacketBuffer[0] = 0xF3; + DevExt->PacketBuffer[1] = I8042GetTypematicByte( + DevExt->KeyboardTypematic.Rate, + DevExt->KeyboardTypematic.Delay); + + I8042StartPacket(DevExt, + DeviceObject, + DevExt->PacketBuffer, + 2, + Irp); + break; + default: + return FALSE; + } + + return TRUE; +} + +/* + * Runs the keyboard IOCTL_INTERNAL dispatch. + * Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request + * so someone else can have a try at it. + */ +NTSTATUS STDCALL I8042InternalDeviceControlKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PIO_STACK_LOCATION Stk; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + + DPRINT("InternalDeviceControl\n"); + + Irp->IoStatus.Information = 0; + Stk = IoGetCurrentIrpStackLocation(Irp); + + switch (Stk->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_INTERNAL_KEYBOARD_CONNECT: + DPRINT("IOCTL_INTERNAL_KEYBOARD_CONNECT\n"); + if (Stk->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CONNECT_DATA)) { + DPRINT1("Keyboard IOCTL_INTERNAL_KEYBOARD_CONNECT " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + goto intcontfailure; + } + + if (!DevExt->KeyboardExists) { + Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED; + goto intcontfailure; + } + + if (DevExt->KeyboardClaimed) { + DPRINT1("IOCTL_INTERNAL_KEYBOARD_CONNECT: " + "Keyboard is already claimed\n"); + Irp->IoStatus.Status = STATUS_SHARING_VIOLATION; + goto intcontfailure; + } + + memcpy(&DevExt->KeyboardData, + Stk->Parameters.DeviceIoControl.Type3InputBuffer, + sizeof(CONNECT_DATA)); + DevExt->KeyboardHook.IsrWritePort = I8042IsrWritePortKbd; + DevExt->KeyboardHook.QueueKeyboardPacket = + I8042QueueKeyboardPacket; + DevExt->KeyboardHook.CallContext = DevExt; + + { + PIO_WORKITEM WorkItem; + PI8042_HOOK_WORKITEM WorkItemData; + + WorkItem = IoAllocateWorkItem(DeviceObject); + if (!WorkItem) { + DPRINT ("IOCTL_INTERNAL_KEYBOARD_CONNECT: " + "Can't allocate work item\n"); + Irp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + goto intcontfailure; + } + + WorkItemData = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(I8042_HOOK_WORKITEM), + TAG_I8042); + if (!WorkItemData) { + DPRINT ("IOCTL_INTERNAL_KEYBOARD_CONNECT: " + "Can't allocate work item data\n"); + Irp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoFreeWorkItem(WorkItem); + goto intcontfailure; + } + WorkItemData->WorkItem = WorkItem; + WorkItemData->Target = + DevExt->KeyboardData.ClassDeviceObject; + WorkItemData->Irp = Irp; + + IoMarkIrpPending(Irp); + IoQueueWorkItem(WorkItem, + I8042SendHookWorkItem, + DelayedWorkQueue, + WorkItemData); + + Irp->IoStatus.Status = STATUS_PENDING; + } + + break; + case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER: + DPRINT("IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER\n"); + if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + goto intcontfailure; + } + if (!DevExt->KeyboardInterruptObject) { + Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY; + goto intcontfailure; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + Irp->IoStatus.Status = STATUS_PENDING; + + break; + case IOCTL_KEYBOARD_QUERY_ATTRIBUTES: + DPRINT("IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n"); + if (Stk->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(KEYBOARD_ATTRIBUTES)) { + DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_ATTRIBUTES " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + goto intcontfailure; + } + memcpy(Irp->AssociatedIrp.SystemBuffer, + &DevExt->KeyboardAttributes, + sizeof(KEYBOARD_ATTRIBUTES)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IOCTL_KEYBOARD_QUERY_INDICATORS: + DPRINT("IOCTL_KEYBOARD_QUERY_INDICATORS\n"); + if (Stk->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(KEYBOARD_INDICATOR_PARAMETERS)) { + DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_INDICATORS " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + goto intcontfailure; + } + memcpy(Irp->AssociatedIrp.SystemBuffer, + &DevExt->KeyboardIndicators, + sizeof(KEYBOARD_INDICATOR_PARAMETERS)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IOCTL_KEYBOARD_QUERY_TYPEMATIC: + DPRINT("IOCTL_KEYBOARD_QUERY_TYPEMATIC\n"); + if (Stk->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) { + DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_TYPEMATIC " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + goto intcontfailure; + } + memcpy(Irp->AssociatedIrp.SystemBuffer, + &DevExt->KeyboardTypematic, + sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IOCTL_KEYBOARD_SET_INDICATORS: + DPRINT("IOCTL_KEYBOARD_SET_INDICATORS\n"); + if (Stk->Parameters.DeviceIoControl.InputBufferLength < + sizeof(KEYBOARD_INDICATOR_PARAMETERS)) { + DPRINT("Keyboard IOCTL_KEYBOARD_SET_INDICTATORS " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + goto intcontfailure; + } + + memcpy(&DevExt->KeyboardIndicators, + Irp->AssociatedIrp.SystemBuffer, + sizeof(KEYBOARD_INDICATOR_PARAMETERS)); + + DPRINT("%x\n", DevExt->KeyboardIndicators.LedFlags); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + Irp->IoStatus.Status = STATUS_PENDING; + + break; + case IOCTL_KEYBOARD_SET_TYPEMATIC: + DPRINT("IOCTL_KEYBOARD_SET_TYPEMATIC\n"); + if (Stk->Parameters.DeviceIoControl.InputBufferLength < + sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) { + DPRINT("Keyboard IOCTL_KEYBOARD_SET_TYPEMATIC " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + goto intcontfailure; + } + + memcpy(&DevExt->KeyboardTypematic, + Irp->AssociatedIrp.SystemBuffer, + sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + Irp->IoStatus.Status = STATUS_PENDING; + + break; + case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: + /* We should check the UnitID, but it's kind of pointless as + * all keyboards are supposed to have the same one + */ + if (Stk->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)) { + DPRINT("IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: " + "invalid buffer size (expected)\n"); + /* It's to query the buffer size */ + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + goto intcontfailure; + } + Irp->IoStatus.Information = + sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION); + + memcpy(Irp->AssociatedIrp.SystemBuffer, + &IndicatorTranslation, + sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD: + /* Nothing to do here */ + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + default: + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + +intcontfailure: + return Irp->IoStatus.Status; +} + +/* This is all pretty confusing. There's more than one way to + * disable/enable the keyboard. You can send KBD_ENABLE to the + * keyboard, and it will start scanning keys. Sending KBD_DISABLE + * will disable the key scanning but also reset the parameters to + * defaults. + * + * You can also send 0xAE to the controller for enabling the + * keyboard clock line and 0xAD for disabling it. Then it'll + * automatically get turned on at the next command. The last + * way is by modifying the bit that drives the clock line in the + * 'command byte' of the controller. This is almost, but not quite, + * the same as the AE/AD thing. The difference can be used to detect + * some really old broken keyboard controllers which I hope won't be + * necessary. + * + * We change the command byte, sending KBD_ENABLE/DISABLE seems to confuse + * some kvm switches. + */ + +BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt) +{ + UCHAR Value; + NTSTATUS Status; + + DPRINT("Enable keyboard\n"); + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value &= ~CCB_KBD_DISAB; // don't disable keyboard + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + return TRUE; +} + +static BOOLEAN STDCALL I8042KeyboardDefaultsAndDisable(PDEVICE_EXTENSION DevExt) +{ + UCHAR Value; + NTSTATUS Status; + + DPRINT("Disabling keyboard\n"); + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value |= CCB_KBD_DISAB; // disable keyboard + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + return TRUE; +} + +BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt) +{ + UCHAR Value; + NTSTATUS Status; + + DPRINT("Enabling keyboard interrupt\n"); + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value &= ~CCB_KBD_DISAB; // don't disable keyboard + Value |= CCB_KBD_INT_ENAB; // enable keyboard interrupts + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + return TRUE; +} + +BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt) +{ + NTSTATUS Status; + UCHAR Value; + UCHAR Value2; + ULONG RetryCount = 10; + + DPRINT("Detecting keyboard\n"); + + I8042KeyboardDefaultsAndDisable(DevExt); + + do { + I8042Flush(); + Status = I8042SynchWritePort(DevExt, 0, KBD_GET_ID, TRUE); + } while (STATUS_IO_TIMEOUT == Status && RetryCount--); + + if (!NT_SUCCESS(Status)) { + DPRINT1("Can't write GET_ID (%x)\n", Status); + /* Could be an AT keyboard */ + DevExt->KeyboardIsAT = TRUE; + goto detectsetleds; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after GET_ID\n"); + /* Could be an AT keyboard */ + DevExt->KeyboardIsAT = TRUE; + goto detectsetleds; + } + DevExt->KeyboardIsAT = FALSE; + + if (Value != 0xAB && Value != 0xAC) { + DPRINT("Bad ID: %x\n", Value); + /* This is certainly not a keyboard */ + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value2); + if (!NT_SUCCESS(Status)) { + DPRINT("Partial ID\n"); + return FALSE; + } + + DPRINT("Keyboard ID: 0x%x 0x%x\n", Value, Value2); + +detectsetleds: + I8042Flush(); /* Flush any bytes left over from GET_ID */ + + Status = I8042SynchWritePort(DevExt, 0, KBD_SET_LEDS, TRUE); + if (!NT_SUCCESS(Status)) { + DPRINT("Can't write SET_LEDS (%x)\n", Status); + return FALSE; + } + Status = I8042SynchWritePort(DevExt, 0, 0, TRUE); + if (!NT_SUCCESS(Status)) { + DPRINT("Can't finish SET_LEDS (%x)\n", Status); + return FALSE; + } + + // Turn on translation + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value |= 0x40; // enable keyboard translation + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + return TRUE; +} + +/* debug stuff */ +VOID STDCALL +KdpServiceDispatcher(ULONG Code, PVOID Context1, PVOID Context2); +#define EnterDebugger ((PVOID)0x25) + +static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject, + PVOID Context) +{ + ULONG Key; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + + Key = InterlockedExchange((PLONG)&DevExt->DebugKey, 0); + DPRINT("Debug key: %x\n", Key); + + if (!Key) + return; + +#ifdef __REACTOS__ + /* We hope kernel would understand this. If + * that's not the case, nothing would happen. + */ + KdpServiceDispatcher(TAG('R', 'o', 's', ' '), (PVOID)Key, NULL); +#endif /* __REACTOS__ */ +} diff --git a/reactos/drivers/input/i8042prt/mouse.c b/reactos/drivers/input/i8042prt/mouse.c new file mode 100644 index 00000000000..5a8afb5ebf3 --- /dev/null +++ b/reactos/drivers/input/i8042prt/mouse.c @@ -0,0 +1,948 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/input/i8042prt/mouse.c + * PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver + * mouse specifics + * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com) + * Jason Filby (jasonfilby@yahoo.com) + * Tinus + */ + +/* INCLUDES ****************************************************************/ + +#include "i8042prt.h" + +#define NDEBUG +#include + +/* + * These functions are callbacks for filter driver custom interrupt + * service routines. + */ +static VOID STDCALL I8042IsrWritePortMouse(PVOID Context, + UCHAR Value) +{ + I8042IsrWritePort(Context, Value, 0xD4); +} + +#if 0 +static NTSTATUS STDCALL I8042SynchWritePortMouse(PVOID Context, + UCHAR Value, + BOOLEAN WaitForAck) +{ + return I8042SynchWritePort((PDEVICE_EXTENSION)Context, + 0xD4, + Value, + WaitForAck); +} +#endif + +/* Test if packets are taking too long to come in. If they do, we + * might have gotten out of sync and should just drop what we have. + * + * If we want to be totally right, we'd also have to keep a count of + * errors, and totally reset the mouse after too much of them (can + * happen if the user is using a KVM switch and an OS on another port + * resets the mouse, or if the user hotplugs the mouse, or if we're just + * generally unlucky). Also note the input parsing routine where we + * drop invalid input packets. + */ +static VOID STDCALL I8042MouseInputTestTimeout(PDEVICE_EXTENSION DevExt) +{ + ULARGE_INTEGER Now; + + if (DevExt->MouseState == MouseExpectingACK || + DevExt->MouseState == MouseResetting) + return; + + Now.QuadPart = KeQueryInterruptTime(); + + if (DevExt->MouseState != MouseIdle) { + /* Check if the last byte came too long ago */ + if (Now.QuadPart - DevExt->MousePacketStartTime.QuadPart > + DevExt->Settings.MouseSynchIn100ns) { + DPRINT("Mouse input packet timeout\n"); + DevExt->MouseState = MouseIdle; + } + } + + if (DevExt->MouseState == MouseIdle) + DevExt->MousePacketStartTime.QuadPart = Now.QuadPart; +} + +/* + * Call the customization hook. The Ret2 parameter is about wether + * we should go on with the interrupt. The return value is what + * we should return (indicating to the system wether someone else + * should try to handle the interrupt) + */ +static BOOLEAN STDCALL I8042MouseCallIsrHook(PDEVICE_EXTENSION DevExt, + UCHAR Status, + PUCHAR Input, + PBOOLEAN ToReturn) +{ + BOOLEAN HookReturn, HookContinue; + + HookContinue = FALSE; + + if (DevExt->MouseHook.IsrRoutine) { + HookReturn = DevExt->MouseHook.IsrRoutine( + DevExt->MouseHook.Context, + DevExt->MouseBuffer + DevExt->MouseInBuffer, + &DevExt->Packet, + Status, + Input, + &HookContinue, + &DevExt->MouseState, + &DevExt->MouseResetState); + + if (!HookContinue) { + *ToReturn = HookReturn; + return TRUE; + } + } + return FALSE; +} + +static BOOLEAN STDCALL I8042MouseResetIsr(PDEVICE_EXTENSION DevExt, + UCHAR Status, + PUCHAR Value) +{ + BOOLEAN ToReturn = FALSE; + + if (I8042MouseCallIsrHook(DevExt, Status, Value, &ToReturn)) + return ToReturn; + + if (MouseResetting != DevExt->MouseState) { + return FALSE; + } + DevExt->MouseTimeoutState = TimeoutStart; + + switch (DevExt->MouseResetState) { + case 1100: /* the first ack, drop it. */ + DevExt->MouseResetState = ExpectingReset; + return TRUE; + /* First, 0xFF is sent. The mouse is supposed to say AA00 if ok, + * FC00 if not. + */ + case ExpectingReset: + if (0xAA == *Value) { + DevExt->MouseResetState++; + } else { + DevExt->MouseExists = FALSE; + DevExt->MouseState = MouseIdle; + DPRINT("Mouse returned bad reset reply: " + "%x (expected aa)\n", *Value); + } + return TRUE; + case ExpectingResetId: + if (0x00 == *Value) { + DevExt->MouseResetState++; + DevExt->MouseType = GenericPS2; + I8042IsrWritePortMouse(DevExt, 0xF2); + } else { + DevExt->MouseExists = FALSE; + DevExt->MouseState = MouseIdle; + DPRINT1("Mouse returned bad reset reply part two: " + "%x (expected 0)\n", *Value); + } + return TRUE; + case ExpectingGetDeviceIdACK: + if (MOUSE_ACK == *Value) { + DevExt->MouseResetState++; + } else if (MOUSE_NACK == *Value || + MOUSE_ERROR == *Value) { + DevExt->MouseResetState++; + /* Act as if 00 (normal mouse) was received */ + DPRINT("Mouse doesn't support 0xd2, " + "(returns %x, expected %x), faking.\n", + *Value, MOUSE_ACK); + *Value = 0; + I8042MouseResetIsr(DevExt, Status, Value); + } + return TRUE; + case ExpectingGetDeviceIdValue: + switch (*Value) { + case 0x02: + DevExt->MouseAttributes.MouseIdentifier = + BALLPOINT_I8042_HARDWARE; + break; + case 0x03: + case 0x04: + DevExt->MouseAttributes.MouseIdentifier = + WHEELMOUSE_I8042_HARDWARE; + break; + default: + DevExt->MouseAttributes.MouseIdentifier = + MOUSE_I8042_HARDWARE; + } + DevExt->MouseResetState++; + I8042IsrWritePortMouse(DevExt, 0xE8); + return TRUE; + case ExpectingSetResolutionDefaultACK: + DevExt->MouseResetState++; + I8042IsrWritePortMouse(DevExt, 0x00); + return TRUE; + case ExpectingSetResolutionDefaultValueACK: + DevExt->MouseResetState = ExpectingSetScaling1to1ACK; + I8042IsrWritePortMouse(DevExt, 0xE6); + return TRUE; + case ExpectingSetScaling1to1ACK: + case ExpectingSetScaling1to1ACK2: + DevExt->MouseResetState++; + I8042IsrWritePortMouse(DevExt, 0xE6); + return TRUE; + case ExpectingSetScaling1to1ACK3: + DevExt->MouseResetState++; + I8042IsrWritePortMouse(DevExt, 0xE9); + return TRUE; + case ExpectingReadMouseStatusACK: + DevExt->MouseResetState++; + return TRUE; + case ExpectingReadMouseStatusByte1: + DevExt->MouseLogiBuffer[0] = *Value; + DevExt->MouseResetState++; + return TRUE; + case ExpectingReadMouseStatusByte2: + DevExt->MouseLogiBuffer[1] = *Value; + DevExt->MouseResetState++; + return TRUE; + case ExpectingReadMouseStatusByte3: + DevExt->MouseLogiBuffer[2] = *Value; + /* Now MouseLogiBuffer is a set of info. If the second + * byte is 0, the mouse didn't understand the magic + * code. Otherwise, it it a Logitech and the second byte + * is the number of buttons, bit 7 of the first byte tells + * if it understands special E7 commands, the rest is an ID. + */ + if (DevExt->MouseLogiBuffer[1]) { + DevExt->MouseAttributes.NumberOfButtons = + DevExt->MouseLogiBuffer[1]; + /* For some reason the ID is the wrong way around */ + DevExt->MouseLogitechID = + ((DevExt->MouseLogiBuffer[0] >> 4) & 0x07) | + ((DevExt->MouseLogiBuffer[0] << 3) & 0x78); + DevExt->MouseType = Ps2pp; + I8042IsrWritePortMouse(DevExt, 0xf3); + DevExt->MouseResetState = ExpectingSetSamplingRateACK; + return TRUE; + /* TODO: Go through EnableWheel and Enable5Buttons */ + } + DevExt->MouseResetState = EnableWheel; + I8042MouseResetIsr(DevExt, Status, Value); + return TRUE; + case EnableWheel: + I8042IsrWritePortMouse(DevExt, 0xf3); + DevExt->MouseResetState = 1001; + return TRUE; + case 1001: + I8042IsrWritePortMouse(DevExt, 0xc8); + DevExt->MouseResetState++; + return TRUE; + case 1002: + case 1004: + I8042IsrWritePortMouse(DevExt, 0xf3); + DevExt->MouseResetState++; + return TRUE; + case 1003: + I8042IsrWritePortMouse(DevExt, 0x64); + DevExt->MouseResetState++; + return TRUE; + case 1005: + I8042IsrWritePortMouse(DevExt, 0x50); + DevExt->MouseResetState++; + return TRUE; + case 1006: + I8042IsrWritePortMouse(DevExt, 0xf2); + DevExt->MouseResetState++; + return TRUE; + case 1007: + /* Ignore ACK */ + DevExt->MouseResetState++; + return TRUE; + case 1008: + /* Now if the value == 3, it's either an Intellimouse + * or Intellimouse Explorer. */ + if (0x03 == *Value) { + DevExt->MouseAttributes.NumberOfButtons = 3; + DevExt->MouseAttributes.MouseIdentifier = + WHEELMOUSE_I8042_HARDWARE; + DevExt->MouseType = Intellimouse; + DevExt->MouseResetState = Enable5Buttons; + I8042MouseResetIsr(DevExt, Status, Value); + return TRUE; + } /* Else, just set the default settings and be done */ + I8042IsrWritePortMouse(DevExt, 0xf3); + DevExt->MouseResetState = ExpectingSetSamplingRateACK; + return TRUE; + case Enable5Buttons: + I8042IsrWritePortMouse(DevExt, 0xf3); + DevExt->MouseResetState = 1021; + return TRUE; + case 1022: + case 1024: + I8042IsrWritePortMouse(DevExt, 0xf3); + DevExt->MouseResetState++; + return TRUE; + case 1021: + case 1023: + I8042IsrWritePortMouse(DevExt, 0xc8); + DevExt->MouseResetState++; + return TRUE; + case 1025: + I8042IsrWritePortMouse(DevExt, 0x50); + DevExt->MouseResetState++; + return TRUE; + case 1026: + I8042IsrWritePortMouse(DevExt, 0xf2); + DevExt->MouseResetState++; + return TRUE; + case 1027: + if (0x04 == *Value) { + DevExt->MouseAttributes.NumberOfButtons = 5; + DevExt->MouseAttributes.MouseIdentifier = + WHEELMOUSE_I8042_HARDWARE; + DevExt->MouseType = IntellimouseExplorer; + } + I8042IsrWritePortMouse(DevExt, 0xf3); + DevExt->MouseResetState = ExpectingSetSamplingRateACK; + return TRUE; + case ExpectingSetSamplingRateACK: + I8042IsrWritePortMouse(DevExt, + (UCHAR)DevExt->MouseAttributes.SampleRate); + DevExt->MouseResetState++; + return TRUE; + case ExpectingSetSamplingRateValueACK: + if (MOUSE_NACK == *Value) { + I8042IsrWritePortMouse(DevExt, 0x3c); + DevExt->MouseAttributes.SampleRate = 60; + DevExt->MouseResetState = 1040; + return TRUE; + } + case 1040: /* Fallthrough */ + I8042IsrWritePortMouse(DevExt, 0xe8); + DevExt->MouseResetState = ExpectingFinalResolutionACK; + return TRUE; + case ExpectingFinalResolutionACK: + I8042IsrWritePortMouse(DevExt, + (UCHAR)(DevExt->Settings.MouseResolution & 0xff)); + DPRINT("%x\n", DevExt->Settings.MouseResolution); + DevExt->MouseResetState = ExpectingFinalResolutionValueACK; + return TRUE; + case ExpectingFinalResolutionValueACK: + I8042IsrWritePortMouse(DevExt, 0xf4); + DevExt->MouseResetState = ExpectingEnableACK; + return TRUE; + case ExpectingEnableACK: + DevExt->MouseState = MouseIdle; + DevExt->MouseTimeoutState = TimeoutCancel; + DPRINT("Mouse type = %d\n", DevExt->MouseType); + return TRUE; + default: + if (DevExt->MouseResetState < 100 || + DevExt->MouseResetState > 999) + DPRINT1("MouseResetState went out of range: %d\n", + DevExt->MouseResetState); + return FALSE; + } +} + +/* + * Prepare for reading the next packet and queue the dpc for handling + * this one. + * + * Context is the device object. + */ +VOID STDCALL I8042QueueMousePacket(PVOID Context) +{ + PDEVICE_OBJECT DeviceObject = Context; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + + DevExt->MouseComplete = TRUE; + DevExt->MouseInBuffer++; + if (DevExt->MouseInBuffer > + DevExt->MouseAttributes.InputDataQueueLength) { + DPRINT1("Mouse buffer overflow\n"); + DevExt->MouseInBuffer--; + } + + DPRINT("Irq completes mouse packet\n"); + KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL); +} + +/* + * Updates ButtonFlags according to RawButtons and a saved state; + * Only takes in account the bits that are set in Mask + */ +VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt, + USHORT Mask) +{ + PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer + + DevExt->MouseInBuffer; + USHORT NewButtonData = (USHORT)(MouseInput->RawButtons & Mask); + USHORT ButtonDiff = (NewButtonData ^ DevExt->MouseButtonState) & Mask; + + /* Note that the defines are such: + * MOUSE_LEFT_BUTTON_DOWN 1 + * MOUSE_LEFT_BUTTON_UP 2 + */ + MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) | + (((~(NewButtonData)) << 1) & + (ButtonDiff << 1)) | + (MouseInput->RawButtons & 0xfc00); + + DPRINT("Left raw/up/down: %d/%d/%d\n", MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN, + MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN, + MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP); + + DevExt->MouseButtonState = (DevExt->MouseButtonState & ~Mask) | + (NewButtonData & Mask); +} + +VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt, + UCHAR Output) +{ + PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer + + DevExt->MouseInBuffer; + CHAR Scroll; + + switch (DevExt->MouseState) { + case MouseIdle: + /* This bit should be 1, if not drop the packet, we + * might be lucky and get in sync again + */ + if (!(Output & 8)) { + DPRINT1("Bad input, dropping..\n"); + return; + } + + MouseInput->Buttons = 0; + MouseInput->RawButtons = 0; + MouseInput->Flags = MOUSE_MOVE_RELATIVE; + + /* Note how we ignore the overflow bits, like Windows + * is said to do. There's no reasonable thing to do + * anyway. + */ + + if (Output & 16) + MouseInput->LastX = 1; + else + MouseInput->LastX = 0; + + if (Output & 32) + MouseInput->LastY = 1; + else + MouseInput->LastY = 0; + + if (Output & 1) { + MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN; + } + + if (Output & 2) { + MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN; + } + + if (Output & 4) { + MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN; + } + DevExt->MouseState = XMovement; + break; + case XMovement: + if (MouseInput->LastX) + MouseInput->LastX = (LONG) Output - 256; + else + MouseInput->LastX = Output; + + DevExt->MouseState = YMovement; + break; + case YMovement: + if (MouseInput->LastY) + MouseInput->LastY = (LONG)Output - 256; + else + MouseInput->LastY = (LONG)Output; + + /* Windows wants it the other way around */ + MouseInput->LastY = -MouseInput->LastY; + + if (DevExt->MouseType == GenericPS2 || + DevExt->MouseType == Ps2pp) { + I8042MouseHandleButtons(DevExt, + MOUSE_LEFT_BUTTON_DOWN | + MOUSE_RIGHT_BUTTON_DOWN | + MOUSE_MIDDLE_BUTTON_DOWN); + I8042QueueMousePacket( + DevExt->MouseObject); + DevExt->MouseState = MouseIdle; + } else { + DevExt->MouseState = ZMovement; + } + break; + case ZMovement: + Scroll = Output & 0x0f; + if (Scroll & 8) + Scroll |= 0xf0; + + if (Scroll) { + MouseInput->RawButtons |= MOUSE_WHEEL; + MouseInput->ButtonData = (USHORT)(Scroll * -WHEEL_DELTA); + } + + if (DevExt->MouseType == IntellimouseExplorer) { + if (Output & 16) + MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN; + + if (Output & 32) + MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN; + } + I8042MouseHandleButtons(DevExt, MOUSE_LEFT_BUTTON_DOWN | + MOUSE_RIGHT_BUTTON_DOWN | + MOUSE_MIDDLE_BUTTON_DOWN | + MOUSE_BUTTON_4_DOWN | + MOUSE_BUTTON_5_DOWN); + I8042QueueMousePacket(DevExt->MouseObject); + DevExt->MouseState = MouseIdle; + break; + default: + DPRINT1("Unexpected state!\n"); + } +} + +BOOLEAN STDCALL I8042InterruptServiceMouse(struct _KINTERRUPT *Interrupt, + VOID *Context) +{ + UCHAR Output, PortStatus; + NTSTATUS Status; + PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context; + ULONG Iterations = 0; + + do { + Status = I8042ReadStatus(&PortStatus); + Status = I8042ReadData(&Output); + Iterations++; + if (STATUS_SUCCESS == Status) + break; + KeStallExecutionProcessor(1); + } while (Iterations < DevExt->Settings.PollStatusIterations); + + if (STATUS_SUCCESS != Status) { + DPRINT1("Spurious I8042 mouse interrupt\n"); + return FALSE; + } + + DPRINT("Got: %x\n", Output); + + if (I8042PacketIsr(DevExt, Output)) { + if (DevExt->PacketComplete) { + DPRINT("Packet complete\n"); + KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL); + } + DPRINT("Irq eaten by packet\n"); + return TRUE; + } + + I8042MouseInputTestTimeout(DevExt); + + if (I8042MouseResetIsr(DevExt, PortStatus, &Output)) { + DPRINT("Handled by ResetIsr or hooked Isr\n"); + if (NoChange != DevExt->MouseTimeoutState) { + KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL); + } + return TRUE; + } + + if (DevExt->MouseType == Ps2pp) + I8042MouseHandlePs2pp(DevExt, Output); + else + I8042MouseHandle(DevExt, Output); + + return TRUE; +} + +VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1; + ULONG MouseTransferred = 0; + ULONG MouseInBufferCopy; + KIRQL Irql; + LARGE_INTEGER Timeout; + + switch (DevExt->MouseTimeoutState) { + case TimeoutStart: + DevExt->MouseTimeoutState = NoChange; + if (DevExt->MouseTimeoutActive && + !KeCancelTimer(&DevExt->TimerMouseTimeout)) { + /* The timer fired already, give up */ + DevExt->MouseTimeoutActive = FALSE; + return; + } + + Timeout.QuadPart = -15000000; + /* 1.5 seconds, should be enough */ + + KeSetTimer(&DevExt->TimerMouseTimeout, + Timeout, + &DevExt->DpcMouseTimeout); + DevExt->MouseTimeoutActive = TRUE; + return; + case TimeoutCancel: + DevExt->MouseTimeoutState = NoChange; + KeCancelTimer(&DevExt->TimerMouseTimeout); + DevExt->MouseTimeoutActive = FALSE; + default: + /* nothing, don't want a warning */ ; + } + + /* Should be unlikely */ + if (!DevExt->MouseComplete) + return; + + Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + + DevExt->MouseComplete = FALSE; + MouseInBufferCopy = DevExt->MouseInBuffer; + + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); + + DPRINT ("Send a mouse packet\n"); + + if (!DevExt->MouseData.ClassService) + return; + + ((PSERVICE_CALLBACK_ROUTINE) DevExt->MouseData.ClassService)( + DevExt->MouseData.ClassDeviceObject, + DevExt->MouseBuffer, + DevExt->MouseBuffer + MouseInBufferCopy, + &MouseTransferred); + + Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + + DevExt->MouseInBuffer -= MouseTransferred; + if (DevExt->MouseInBuffer) + RtlMoveMemory(DevExt->MouseBuffer, + DevExt->MouseBuffer+MouseTransferred, + DevExt->MouseInBuffer * sizeof(MOUSE_INPUT_DATA)); + + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); +} + +/* This timer DPC will be called when the mouse reset times out. + * I'll just send the 'disable mouse port' command to the controller + * and say the mouse doesn't exist. + */ +VOID STDCALL I8042DpcRoutineMouseTimeout(PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)DeferredContext; + KIRQL Irql; + + Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + + DPRINT1("Mouse initialization timeout! (substate %x) " + "Disabling mouse.\n", + DevExt->MouseResetState); + + if (!I8042MouseDisable(DevExt)) { + DPRINT1("Failed to disable mouse.\n"); + } + + DevExt->MouseExists = FALSE; + + KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); +} + +/* + * Process the mouse internal device requests + * returns FALSE if it doesn't understand the + * call so someone else can handle it. + */ +#if 0 +static BOOLEAN STDCALL I8042StartIoMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PIO_STACK_LOCATION Stk; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + + Stk = IoGetCurrentIrpStackLocation(Irp); + + switch (Stk->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER: + I8042StartPacket( + DevExt, + DeviceObject, + Stk->Parameters.DeviceIoControl.Type3InputBuffer, + Stk->Parameters.DeviceIoControl.InputBufferLength, + Irp); + break; + + default: + return FALSE; + } + + return TRUE; +} +#endif + +/* + * Runs the mouse IOCTL_INTERNAL dispatch. + * Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request + * so someone else can have a try at it. + */ +NTSTATUS STDCALL I8042InternalDeviceControlMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PIO_STACK_LOCATION Stk; + PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + + DPRINT("InternalDeviceControl\n"); + + Irp->IoStatus.Information = 0; + Stk = IoGetCurrentIrpStackLocation(Irp); + + switch (Stk->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_INTERNAL_MOUSE_CONNECT: + DPRINT("IOCTL_INTERNAL_MOUSE_CONNECT\n"); + if (Stk->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CONNECT_DATA)) { + DPRINT1("Mouse IOCTL_INTERNAL_MOUSE_CONNECT " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + goto intcontfailure; + } + + if (!DevExt->MouseExists) { + Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED; + goto intcontfailure; + } + + if (DevExt->MouseClaimed) { + DPRINT1("IOCTL_INTERNAL_MOUSE_CONNECT: " + "Mouse is already claimed\n"); + Irp->IoStatus.Status = STATUS_SHARING_VIOLATION; + goto intcontfailure; + } + + memcpy(&DevExt->MouseData, + Stk->Parameters.DeviceIoControl.Type3InputBuffer, + sizeof(CONNECT_DATA)); + DevExt->MouseHook.IsrWritePort = I8042IsrWritePortMouse; + DevExt->MouseHook.QueueMousePacket = + I8042QueueMousePacket; + DevExt->MouseHook.CallContext = DevExt; + + { + PIO_WORKITEM WorkItem; + PI8042_HOOK_WORKITEM WorkItemData; + + WorkItem = IoAllocateWorkItem(DeviceObject); + if (!WorkItem) { + DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: " + "Can't allocate work item\n"); + Irp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + goto intcontfailure; + } + + WorkItemData = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(I8042_HOOK_WORKITEM), + TAG_I8042); + if (!WorkItemData) { + DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: " + "Can't allocate work item data\n"); + Irp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoFreeWorkItem(WorkItem); + goto intcontfailure; + } + WorkItemData->WorkItem = WorkItem; + WorkItemData->Target = + DevExt->MouseData.ClassDeviceObject; + WorkItemData->Irp = Irp; + + IoMarkIrpPending(Irp); + DevExt->MouseState = MouseResetting; + DevExt->MouseResetState = 1100; + IoQueueWorkItem(WorkItem, + I8042SendHookWorkItem, + DelayedWorkQueue, + WorkItemData); + + Irp->IoStatus.Status = STATUS_PENDING; + } + + break; + case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER: + DPRINT("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER\n"); + if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + goto intcontfailure; + } + if (!DevExt->MouseInterruptObject) { + Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY; + goto intcontfailure; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + Irp->IoStatus.Status = STATUS_PENDING; + + break; + case IOCTL_MOUSE_QUERY_ATTRIBUTES: + DPRINT("IOCTL_MOUSE_QUERY_ATTRIBUTES\n"); + if (Stk->Parameters.DeviceIoControl.InputBufferLength < + sizeof(MOUSE_ATTRIBUTES)) { + DPRINT("Mouse IOCTL_MOUSE_QUERY_ATTRIBUTES " + "invalid buffer size\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + goto intcontfailure; + } + memcpy(Irp->AssociatedIrp.SystemBuffer, + &DevExt->MouseAttributes, + sizeof(MOUSE_ATTRIBUTES)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + default: + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + +intcontfailure: + return Irp->IoStatus.Status; +} + +BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt) +{ + UCHAR Value; + NTSTATUS Status; + + DPRINT("Enabling mouse\n"); + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value &= ~(0x20); // don't disable mouse + Value |= 0x02; // enable mouse interrupts + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + return TRUE; +} + +BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt) +{ + UCHAR Value; + NTSTATUS Status; + + DPRINT("Disabling mouse\n"); + + I8042Flush(); /* Just to be (kind of) sure */ + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = I8042ReadDataWait(DevExt, &Value); + if (!NT_SUCCESS(Status)) { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value |= 0x20; // don't disable mouse + Value &= ~(0x02); // enable mouse interrupts + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + I8042Flush(); + /* Just to be (kind of) sure; if the mouse would + * say something while we are disabling it, these bytes would + * block the keyboard. + */ + + return TRUE; +} + +BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt) +{ + BOOLEAN Ok = TRUE; + NTSTATUS Status; + UCHAR Value; + UCHAR ExpectedReply[] = { 0xFA, 0xAA, 0x00 }; + unsigned ReplyByte; + + if (! I8042Write(DevExt, I8042_CTRL_PORT, 0xD4) || + ! I8042Write(DevExt, I8042_DATA_PORT, 0xFF)) + { + DPRINT1("Failed to write reset command to mouse\n"); + Ok = FALSE; + } + + for (ReplyByte = 0; + ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]) && Ok; + ReplyByte++) + { + Status = I8042ReadDataWait(DevExt, &Value); + if (! NT_SUCCESS(Status)) + { + DPRINT1("No ACK after mouse reset, status 0x%08x\n", + Status); + Ok = FALSE; + } + else if (Value != ExpectedReply[ReplyByte]) + { + DPRINT1("Unexpected reply: 0x%02x (expected 0x%02x)\n", + Value, ExpectedReply[ReplyByte]); + Ok = FALSE; + } + } + + if (! Ok) + { + /* There is probably no mouse present. On some systems, + the probe locks the entire keyboard controller. Let's + try to get access to the keyboard again by sending a + reset */ + I8042Flush(); + I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST); + I8042ReadDataWait(DevExt, &Value); + I8042Flush(); + } + + DPRINT("Mouse %sdetected\n", Ok ? "" : "not "); + + return Ok; +} diff --git a/reactos/drivers/input/i8042prt/ps2pp.c b/reactos/drivers/input/i8042prt/ps2pp.c new file mode 100644 index 00000000000..3b183ec8617 --- /dev/null +++ b/reactos/drivers/input/i8042prt/ps2pp.c @@ -0,0 +1,137 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/input/i8042prt/ps2pp.c + * PURPOSE: i8042prt driver + * ps2pp protocol handling + * PROGRAMMER: Tinus + */ + +/* INCLUDES ****************************************************************/ + +#ifndef NDEBUG +#define NDEBUG +#endif +#include + +#include "i8042prt.h" + + +VOID I8042MouseHandlePs2pp(PDEVICE_EXTENSION DevExt, UCHAR Input) +{ + UCHAR PktType; + PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer + + DevExt->MouseInBuffer; + +/* First, collect 3 bytes for a packet + * We can detect out-of-sync only by checking + * the whole packet anyway. + * + * If bit 7 and 8 of the first byte are 0, its + * a normal packet. + * + * Otherwise, the packet is different, like this: + * 1: E 1 b3 b2 x x x x + * 2: x x b1 b0 x1 x0 1 0 + * 3: x x x x x x x1 x0 + * + * b3-0 form a code that specifies the packet type: + * + * 0 Device Type + * 1 Rollers and buttons + * 2 Reserved + * 3 Reserved + * 4 Device ID + * 5 Channel & Battery + * 6 Wireless notifications + * 7 Reserved + * 8 ShortID LSB (ShortID is a number that is supposed to differentiate + * 9 ShortID MSB between your mouse and your neighbours') + * 10 Reserved + * 11 Mouse capabilities + * 12 Remote control LSB + * 13 Remote control MSB + * 14 Reserved + * 15 Extended packet + */ + + switch (DevExt->MouseState) { + case MouseIdle: + case XMovement: + DevExt->MouseLogiBuffer[DevExt->MouseState] = Input; + DevExt->MouseState++; + break; + + case YMovement: + DevExt->MouseLogiBuffer[2] = Input; + DevExt->MouseState = MouseIdle; + + /* first check if it's a normal packet */ + + if (!(DevExt->MouseLogiBuffer[0] & 0xC0)) { + DevExt->MouseState = MouseIdle; + I8042MouseHandle(DevExt, DevExt->MouseLogiBuffer[0]); + I8042MouseHandle(DevExt, DevExt->MouseLogiBuffer[1]); + I8042MouseHandle(DevExt, DevExt->MouseLogiBuffer[2]); + /* We could care about wether MouseState really + * advances, but we don't need to because we're + * only doing three bytes anyway, so the packet + * will never complete if it's broken. + */ + return; + } + + /* sanity check */ + + if (((DevExt->MouseLogiBuffer[0] & 0x48) != 0x48) || + (((DevExt->MouseLogiBuffer[1] & 0x0C) >> 2) != + (DevExt->MouseLogiBuffer[2] & 0x03))) { + DPRINT1("Ps2pp packet fails sanity checks\n"); + return; + } + + /* Now get the packet type */ + + PktType = ((DevExt->MouseLogiBuffer[0] & 0x30) >> 4) & + ((DevExt->MouseLogiBuffer[1] & 0x30) >> 6); + + switch (PktType) { + case 0: + /* The packet contains the device ID, but we + * already read that in the initialization + * sequence. Ignore it. + */ + return; + case 1: + RtlZeroMemory(MouseInput, sizeof(MOUSE_INPUT_DATA)); + if (DevExt->MouseLogiBuffer[2] & 0x10) + MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN; + + if (DevExt->MouseLogiBuffer[2] & 0x20) + MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN; + + if (DevExt->MouseLogiBuffer[2] & 0x0F) { + MouseInput->ButtonFlags |= MOUSE_WHEEL; + if (DevExt->MouseLogiBuffer[2] & 0x08) + MouseInput->ButtonData = + (DevExt->MouseLogiBuffer[2] & 0x07) - + 8; + else + MouseInput->ButtonData = + DevExt->MouseLogiBuffer[2] & 0x07; + } + I8042MouseHandleButtons(DevExt, MOUSE_BUTTON_4_DOWN | + MOUSE_BUTTON_5_DOWN); + I8042QueueMousePacket( + DevExt->MouseObject); + return; + default: + /* These are for things that would probably + * be handled by logitechs own driver. + */ + return; + } + default: + DPRINT1("Unexpected input state for ps2pp!\n"); + } +} diff --git a/reactos/drivers/input/i8042prt/registry.c b/reactos/drivers/input/i8042prt/registry.c new file mode 100644 index 00000000000..8c25872cd31 --- /dev/null +++ b/reactos/drivers/input/i8042prt/registry.c @@ -0,0 +1,211 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/input/i8042prt/registry.c + * PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver + * Reading the registry + * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com) + * Jason Filby (jasonfilby@yahoo.com) + * Tinus + */ + +/* INCLUDES ****************************************************************/ + +#ifndef NDEBUG +#define NDEBUG +#endif +#include + +#include "i8042prt.h" + +/* FUNCTIONS *****************************************************************/ + +/* + * Read the registry keys associated with this device. The RegistryPath + * var is a hack. This should be more like what microsoft does, but I + * don't know exactly what they do except that it's a hack too... + */ +VOID STDCALL I8042ReadRegistry(PDRIVER_OBJECT DriverObject, + PDEVICE_EXTENSION DevExt) + +{ + RTL_QUERY_REGISTRY_TABLE Parameters[19]; + + NTSTATUS Status; + + ULONG DefaultHeadless = 0; + ULONG DefaultCrashScroll = 0; + ULONG DefaultCrashSysRq = 0; + ULONG DefaultReportResetErrors = 0; + ULONG DefaultPollStatusIterations = 1; + ULONG DefaultResendIterations = 3; + ULONG DefaultPollingIterations = 12000; + ULONG DefaultPollingIterationsMaximum = 12000; + ULONG DefaultKeyboardDataQueueSize = 100; + ULONG DefaultOverrideKeyboardType = 0; + ULONG DefaultOverrideKeyboardSubtype = 0; + ULONG DefaultMouseDataQueueSize = 100; + ULONG DefaultMouseResendStallTime = 1000; + ULONG DefaultMouseSynchIn100ns = 20000000; + ULONG DefaultMouseResolution = 3; + ULONG DefaultSampleRate = 60; + ULONG DefaultNumberOfButtons = 2; + ULONG DefaultEnableWheelDetection = 1; + + RtlZeroMemory(Parameters, sizeof(Parameters)); + + Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[0].Name = L"Headless"; + Parameters[0].EntryContext = &DevExt->Settings.Headless; + Parameters[0].DefaultType = REG_DWORD; + Parameters[0].DefaultData = &DefaultHeadless; + Parameters[0].DefaultLength = sizeof(ULONG); + + Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[1].Name = L"CrashOnCtrlScroll"; + Parameters[1].EntryContext = &DevExt->Settings.CrashScroll; + Parameters[1].DefaultType = REG_DWORD; + Parameters[1].DefaultData = &DefaultCrashScroll; + Parameters[1].DefaultLength = sizeof(ULONG); + + Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[2].Name = L"BreakOnSysRq"; + Parameters[2].EntryContext = &DevExt->Settings.CrashSysRq; + Parameters[2].DefaultType = REG_DWORD; + Parameters[2].DefaultData = &DefaultCrashSysRq; + Parameters[2].DefaultLength = sizeof(ULONG); + + Parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[3].Name = L"ReportResetErrors"; + Parameters[3].EntryContext = &DevExt->Settings.ReportResetErrors; + Parameters[3].DefaultType = REG_DWORD; + Parameters[3].DefaultData = &DefaultReportResetErrors; + Parameters[3].DefaultLength = sizeof(ULONG); + + Parameters[4].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[4].Name = L"PollStatusIterations"; + Parameters[4].EntryContext = &DevExt->Settings.PollStatusIterations; + Parameters[4].DefaultType = REG_DWORD; + Parameters[4].DefaultData = &DefaultPollStatusIterations; + Parameters[4].DefaultLength = sizeof(ULONG); + + Parameters[5].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[5].Name = L"ResendIterations"; + Parameters[5].EntryContext = &DevExt->Settings.ResendIterations; + Parameters[5].DefaultType = REG_DWORD; + Parameters[5].DefaultData = &DefaultResendIterations; + Parameters[5].DefaultLength = sizeof(ULONG); + + Parameters[6].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[6].Name = L"PollingIterations"; + Parameters[6].EntryContext = &DevExt->Settings.PollingIterations; + Parameters[6].DefaultType = REG_DWORD; + Parameters[6].DefaultData = &DefaultPollingIterations; + Parameters[6].DefaultLength = sizeof(ULONG); + + Parameters[7].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[7].Name = L"PollingIterationsMaximum"; + Parameters[7].EntryContext = &DevExt->Settings.PollingIterationsMaximum; + Parameters[7].DefaultType = REG_DWORD; + Parameters[7].DefaultData = &DefaultPollingIterationsMaximum; + Parameters[7].DefaultLength = sizeof(ULONG); + + Parameters[8].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[8].Name = L"KeyboardDataQueueSize"; + Parameters[8].EntryContext = + &DevExt->KeyboardAttributes.InputDataQueueLength; + Parameters[8].DefaultType = REG_DWORD; + Parameters[8].DefaultData = &DefaultKeyboardDataQueueSize; + Parameters[8].DefaultLength = sizeof(ULONG); + + Parameters[9].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[9].Name = L"OverrideKeyboardType"; + Parameters[9].EntryContext = &DevExt->Settings.OverrideKeyboardType; + Parameters[9].DefaultType = REG_DWORD; + Parameters[9].DefaultData = &DefaultOverrideKeyboardType; + Parameters[9].DefaultLength = sizeof(ULONG); + + Parameters[10].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[10].Name = L"OverrideKeyboardSubtype"; + Parameters[10].EntryContext = &DevExt->Settings.OverrideKeyboardSubtype; + Parameters[10].DefaultType = REG_DWORD; + Parameters[10].DefaultData = &DefaultOverrideKeyboardSubtype; + Parameters[10].DefaultLength = sizeof(ULONG); + + Parameters[11].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[11].Name = L"MouseDataQueueSize"; + Parameters[11].EntryContext = + &DevExt->MouseAttributes.InputDataQueueLength; + Parameters[11].DefaultType = REG_DWORD; + Parameters[11].DefaultData = &DefaultMouseDataQueueSize; + Parameters[11].DefaultLength = sizeof(ULONG); + + Parameters[12].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[12].Name = L"MouseResendStallTime"; + Parameters[12].EntryContext = &DevExt->Settings.MouseResendStallTime; + Parameters[12].DefaultType = REG_DWORD; + Parameters[12].DefaultData = &DefaultMouseResendStallTime; + Parameters[12].DefaultLength = sizeof(ULONG); + + Parameters[13].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[13].Name = L"MouseSynchIn100ns"; + Parameters[13].EntryContext = &DevExt->Settings.MouseSynchIn100ns; + Parameters[13].DefaultType = REG_DWORD; + Parameters[13].DefaultData = &DefaultMouseSynchIn100ns; + Parameters[13].DefaultLength = sizeof(ULONG); + + Parameters[14].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[14].Name = L"MouseResolution"; + Parameters[14].EntryContext = &DevExt->Settings.MouseResolution; + Parameters[14].DefaultType = REG_DWORD; + Parameters[14].DefaultData = &DefaultMouseResolution; + Parameters[14].DefaultLength = sizeof(ULONG); + + Parameters[15].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[15].Name = L"SampleRate"; + Parameters[15].EntryContext = &DevExt->MouseAttributes.SampleRate; + Parameters[15].DefaultType = REG_DWORD; + Parameters[15].DefaultData = &DefaultSampleRate; + Parameters[15].DefaultLength = sizeof(ULONG); + + Parameters[16].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[16].Name = L"NumberOfButtons"; + Parameters[16].EntryContext = &DevExt->Settings.NumberOfButtons; + Parameters[16].DefaultType = REG_DWORD; + Parameters[16].DefaultData = &DefaultNumberOfButtons; + Parameters[16].DefaultLength = sizeof(ULONG); + + Parameters[17].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[17].Name = L"EnableWheelDetection"; + Parameters[17].EntryContext = &DevExt->Settings.EnableWheelDetection; + Parameters[17].DefaultType = REG_DWORD; + Parameters[17].DefaultData = &DefaultEnableWheelDetection; + Parameters[17].DefaultLength = sizeof(ULONG); + + Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + I8042RegistryPath.Buffer, + Parameters, + NULL, + NULL); + + if (!NT_SUCCESS(Status)) { + /* Actually, the defaults are not set when the function + * fails, as would happen during setup, so you have to + * set them manually anyway... + */ + RTL_QUERY_REGISTRY_TABLE *Current = Parameters; + DPRINT1 ("Can't read registry: %x\n", Status); + while (Current->Name) { + *((PULONG)Current->EntryContext) = + *((PULONG)Current->DefaultData); + Current++; + } + DPRINT1 ("Manually set defaults\n"); + + } + + if (DevExt->Settings.MouseResolution > 3) + DevExt->Settings.MouseResolution = 3; + + DPRINT("Done reading registry\n"); +}