i8042prt driver by tinus. Modified: trunk/reactos/Makefile Modified: trunk/reactos/boot/freeldr/freeldr/reactos/setupldr.c Modified: trunk/reactos/bootdata/hivesys.inf Modified: trunk/reactos/bootdata/txtsetup.sif Modified: trunk/reactos/drivers/input/Makefile Added: trunk/reactos/drivers/input/i8042prt/ Added: trunk/reactos/drivers/input/i8042prt/README.txt Added: trunk/reactos/drivers/input/i8042prt/i8042prt.c Added: trunk/reactos/drivers/input/i8042prt/i8042prt.h Added: trunk/reactos/drivers/input/i8042prt/i8042prt.rc Added: trunk/reactos/drivers/input/i8042prt/keyboard.c Added: trunk/reactos/drivers/input/i8042prt/makefile Added: trunk/reactos/drivers/input/i8042prt/mouse.c Added: trunk/reactos/drivers/input/i8042prt/ps2pp.c Added: trunk/reactos/drivers/input/i8042prt/registry.c Added: trunk/reactos/drivers/input/kbdclass/ Added: trunk/reactos/drivers/input/kbdclass/kbdclass.c Added: trunk/reactos/drivers/input/kbdclass/kbdclass.h Added: trunk/reactos/drivers/input/kbdclass/kbdclass.rc Added: trunk/reactos/drivers/input/kbdclass/makefile Modified: trunk/reactos/drivers/input/mouclass/mouclass.c Modified: trunk/reactos/include/ddk/ntdd8042.h Modified: trunk/reactos/ntoskrnl/include/internal/kd.h Modified: trunk/reactos/subsys/system/usetup/bootsup.c Modified: trunk/reactos/subsys/system/usetup/console.c Added: trunk/reactos/subsys/system/usetup/keytrans.c Added: trunk/reactos/subsys/system/usetup/keytrans.h Modified: trunk/reactos/subsys/system/usetup/makefile Modified: trunk/reactos/subsys/win32k/eng/device.c Modified: trunk/reactos/subsys/win32k/include/tags.h Modified: trunk/reactos/subsys/win32k/ntuser/input.c Modified: trunk/reactos/tools/helper.mk Modified: trunk/reactos/w32api/include/ddk/winddk.h _____
Modified: trunk/reactos/Makefile --- trunk/reactos/Makefile 2005-05-01 20:30:41 UTC (rev 14925) +++ trunk/reactos/Makefile 2005-05-01 20:38:29 UTC (rev 14926) @@ -84,7 +84,7 @@
DEVICE_DRIVERS = beep blue debugout null serial bootvid
# Kernel mode input drivers -INPUT_DRIVERS = keyboard mouclass psaux sermouse +INPUT_DRIVERS = keyboard mouclass psaux sermouse i8042prt kbdclass
# Kernel mode file system drivers # cdfs ext2 fs_rec ms np vfat _____
Modified: trunk/reactos/boot/freeldr/freeldr/reactos/setupldr.c --- trunk/reactos/boot/freeldr/freeldr/reactos/setupldr.c 2005-05-01 20:30:41 UTC (rev 14925) +++ trunk/reactos/boot/freeldr/freeldr/reactos/setupldr.c 2005-05-01 20:38:29 UTC (rev 14926) @@ -558,8 +558,14 @@
/* Load keyboard driver */ +#if 0 if (!LoadDriver(SourcePath, "keyboard.sys")) return; +#endif + if (!LoadDriver(SourcePath, "i8042prt.sys")) + return; + if (!LoadDriver(SourcePath, "kbdclass.sys")) + return;
/* Load screen driver */ if (!LoadDriver(SourcePath, "blue.sys")) _____
Modified: trunk/reactos/bootdata/hivesys.inf --- trunk/reactos/bootdata/hivesys.inf 2005-05-01 20:30:41 UTC (rev 14925) +++ trunk/reactos/bootdata/hivesys.inf 2005-05-01 20:38:29 UTC (rev 14926) @@ -575,12 +575,29 @@
;HKLM,"SYSTEM\CurrentControlSet\Services\Ide","Type",0x00010001,0x000000 01
; Keyboard driver -HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","ErrorControl",0x0001 0001,0x00000000 -HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","Group",0x00000000,"K eyboard Port" -HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","ImagePath",0x0002000 0,"system32\drivers\keyboard.sys" -HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","Start",0x00010001,0x 00000001 -HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","Type",0x00010001,0x0 0000001 +;HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","ErrorControl",0x000 10001,0x00000000 +;HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","Group",0x00000000," Keyboard Port" +;HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","ImagePath",0x000200 00,"system32\drivers\keyboard.sys" +;HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","Start",0x00010001,0 x00000001 +;HKLM,"SYSTEM\CurrentControlSet\Services\Keyboard","Type",0x00010001,0x 00000001
+; i8042 port driver +HKLM,"SYSTEM\CurrentControlSet\Services\i8042Prt","ErrorControl",0x0001 0001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\i8042Prt","Group",0x00000000,"K eyboard Port" +HKLM,"SYSTEM\CurrentControlSet\Services\i8042Prt","ImagePath",0x0002000 0,"system32\drivers\i8042prt.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\i8042Prt","Start",0x00010001,0x 00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\i8042Prt","Type",0x00010001,0x0 0000001 + +HKLM,"SYSTEM\CurrentControlSet\Services\i8042Prt\Parameters","SampleRat e",0x00010001,0x00000060 +HKLM,"SYSTEM\CurrentControlSet\Services\i8042Prt\Parameters","BreakOnSy sRq",0x00010001,0x00000001 + +; Keyboard class driver +HKLM,"SYSTEM\CurrentControlSet\Services\kbdclass","ErrorControl",0x0001 0001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\kbdclass","Group",0x00000000,"K eyboard Class" +HKLM,"SYSTEM\CurrentControlSet\Services\kbdclass","ImagePath",0x0002000 0,"system32\drivers\kbdclass.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\kbdclass","Start",0x00010001,0x 00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\kbdclass","Type",0x00010001,0x0 0000001 + ; Serial port enumerator
HKLM,"SYSTEM\CurrentControlSet\Services\serenum","ErrorControl",0x000100 01,0x00000001
HKLM,"SYSTEM\CurrentControlSet\Services\serenum","Group",0x00000000,"PNP Filter" @@ -840,12 +857,12 @@
HKLM,"SYSTEM\CurrentControlSet\Services\PlugPlay","Type",0x00010001,0x00 000010
; PS/2 mouse port driver -HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","ErrorControl",0x0001000 1,0x00000000 -HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","Group",0x00000000,"Poin ter Port" -HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","ImagePath",0x00020000," system32\drivers\psaux.sys" -HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","Start",0x00010001,0x000 00004 -HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","Type",0x00010001,0x0000 0001 -HKLM,"SYSTEM\CurrentControlSet\Services\Psaux\Parameters","DisableExten sionDetection",0x00010001,0x00000000 +;HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","ErrorControl",0x000100 01,0x00000000 +;HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","Group",0x00000000,"Poi nter Port" +;HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","ImagePath",0x00020000, "system32\drivers\psaux.sys" +;HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","Start",0x00010001,0x00 000004 +;HKLM,"SYSTEM\CurrentControlSet\Services\Psaux","Type",0x00010001,0x000 00001 +;HKLM,"SYSTEM\CurrentControlSet\Services\Psaux\Parameters","DisableExte nsionDetection",0x00010001,0x00000000
; RPC service
HKLM,"SYSTEM\CurrentControlSet\Services\Rpcss","ErrorControl",0x00010001 ,0x00000001 _____
Modified: trunk/reactos/bootdata/txtsetup.sif --- trunk/reactos/bootdata/txtsetup.sif 2005-05-01 20:30:41 UTC (rev 14925) +++ trunk/reactos/bootdata/txtsetup.sif 2005-05-01 20:38:29 UTC (rev 14926) @@ -22,7 +22,9 @@
class2.sys = 3 disk.sys = 3 floppy.sys = 3 -keyboard.sys = 3 +;keyboard.sys = 3 +i8042prt.sys = 3 +kbdclass.sys = 3 l_intl.nls = 2 ntfs.sys = 3 ntoskrnl.exe = 2 @@ -96,14 +98,16 @@
[Mouse] ;<id> = <user friendly name>,<spare>,<service key name> -msps2 = "Microsoft PS2 Mouse",,psaux +i8042ps2 = "PS2 Mouse",,i8042prt +;msps2 = "Microsoft PS2 Mouse",,psaux msser = "Microsoft Serial Mouse",,sermouse mswhs = "Microsoft Serial Wheel Mouse",,sermouse none = "No Mouse"
[Map.Mouse] ;<id> = <pnp id string> -msps2 = "MICROSOFT PS2 MOUSE" +i8042ps2 = "MICROSOFT PS2 MOUSE" +;msps2 = "MICROSOFT PS2 MOUSE" msser = "MICROSOFT SERIAL MOUSE" mswhs = "MICROSOFT MOUSE WITH WHEEL" none = "NO MOUSE" _____
Modified: trunk/reactos/drivers/input/Makefile --- trunk/reactos/drivers/input/Makefile 2005-05-01 20:30:41 UTC (rev 14925) +++ trunk/reactos/drivers/input/Makefile 2005-05-01 20:38:29 UTC (rev 14926) @@ -6,7 +6,7 @@
include $(PATH_TO_TOP)/rules.mak
-DRIVERS = keyboard mouclass psaux sermouse +DRIVERS = keyboard mouclass psaux sermouse i8042prt kbdclass
all: $(DRIVERS)
_____
Added: trunk/reactos/drivers/input/i8042prt/README.txt --- trunk/reactos/drivers/input/i8042prt/README.txt 2005-05-01 20:30:41 UTC (rev 14925) +++ trunk/reactos/drivers/input/i8042prt/README.txt 2005-05-01 20:38:29 UTC (rev 14926) @@ -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/re skit/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. _____
Added: trunk/reactos/drivers/input/i8042prt/i8042prt.c --- trunk/reactos/drivers/input/i8042prt/i8042prt.c 2005-05-01 20:30:41 UTC (rev 14925) +++ trunk/reactos/drivers/input/i8042prt/i8042prt.c 2005-05-01 20:38:29 UTC (rev 14926) @@ -0,0 +1,804 @@
+/* + * 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 ****************************************************************/ + +#include <ddk/ntddk.h> +#include <string.h> +#include <ntos/keyboard.h> +#include <ntos/minmax.h> +#include <rosrtl/string.h> + +#include <ddk/ntddkbd.h> +#include <ddk/ntdd8042.h> + +#define NDEBUG +#include <debug.h> + +#include "i8042prt.h" + +/* GLOBALS *******************************************************************/ + +/* + * Driver data + */ +#define I8042_TIMEOUT 500000 + +#define I8042_MAX_COMMAND_LENGTH 16 +#define I8042_MAX_UPWARDS_STACK 5 + +/* FUNCTIONS *****************************************************************/ + +/* + * FUNCTION: Write data to a port, waiting first for it to become ready + */ +BOOLEAN I8042Write(PDEVICE_EXTENSION DevExt, int addr, BYTE data) +{ + ULONG ResendIterations = DevExt->Settings.PollingIterations; + + while ((KBD_IBF & READ_PORT_UCHAR((PUCHAR)I8042_CTRL_PORT)) && + (ResendIterations--)) + { + KeStallExecutionProcessor(50); + } + + if (ResendIterations) { + WRITE_PORT_UCHAR((PUCHAR)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, BYTE 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(BYTE *Data) +{ + BYTE Status; + Status=READ_PORT_UCHAR((PUCHAR)I8042_CTRL_PORT); + + // If data is available + if ((Status & KBD_OBF)) { + Data[0]=READ_PORT_UCHAR((PUCHAR)I8042_DATA_PORT); + + // If the data is valid (not timeout, not parity error) + if ((~Status) & (KBD_GTO | KBD_PERR)) + return STATUS_SUCCESS; + } + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS I8042ReadStatus(BYTE *Status) +{ + Status[0]=READ_PORT_UCHAR((PUCHAR)I8042_CTRL_PORT); + return STATUS_SUCCESS; +} + +/* + * FUNCTION: Read data from port 0x60 + */ +NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, BYTE *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() +{ + BYTE Ignore; + + while (STATUS_SUCCESS == I8042ReadData(&Ignore)) { + ; /* 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; + UINT ResendIterations = DevExt->Settings.ResendIterations + 1; + + do { + if (Port) + if (!I8042Write(DevExt, I8042_DATA_PORT, Port)) + return STATUS_TIMEOUT; + + if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) + return STATUS_TIMEOUT; + + if (WaitForAck) { + Status = I8042ReadDataWait(DevExt, &Ack); + if (Status != STATUS_SUCCESS) + return Status; + if (Ack == KBD_ACK) + return STATUS_SUCCESS; + if (Ack != KBD_RESEND) + return STATUS_UNEXPECTED_IO_ERROR; + } else { + return STATUS_SUCCESS; + } + ResendIterations--; + } while (ResendIterations); + return STATUS_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_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_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_TIMEOUT; + return TRUE; + } + DevExt->Packet.CurrentByte++; + + return TRUE; +} + +VOID I8042PacketDpc(PDEVICE_EXTENSION DevExt) +{ + BOOL 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; + } + + Status = IoCallDriver( + WorkItemData->Target, + NewIrp); + + if (STATUS_PENDING == Status) + KeWaitForSingleObject(&Event, + Executive, + KernelMode, + FALSE, + NULL); + + 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 (Status != STATUS_SUCCESS) { + 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"); +} + +VOID STDCALL I8042StartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + if (!I8042StartIoKbd(DeviceObject, Irp)) { + DPRINT1("Unhandled StartIo!\n"); + } +} + +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; +} + +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; + UINT Counter; + + I8042Flush(); + + if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST)) + return STATUS_TIMEOUT; + + // Wait longer? + Counter = 3; + do { + Status = I8042ReadDataWait(DevExt, &Value); + } while ((Counter--) && (STATUS_TIMEOUT == Status)); + + if (Status != STATUS_SUCCESS) + 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_LINE_TEST)) + return STATUS_TIMEOUT; + + Status = I8042ReadDataWait(DevExt, &Value); + if (Status != STATUS_SUCCESS) + return Status; + + if (Value == 0) { + DevExt->KeyboardExists = TRUE; + } else { + DevExt->KeyboardExists = FALSE; + } + + if (!I8042Write(DevExt, I8042_CTRL_PORT, MOUSE_LINE_TEST)) + return STATUS_TIMEOUT; + + Status = I8042ReadDataWait(DevExt, &Value); + if (Status != STATUS_SUCCESS) + return Status; + + if (Value == 0) { + DevExt->MouseExists = TRUE; + } else { + DevExt->MouseExists = FALSE; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS STDCALL I8042Initialize(PDEVICE_EXTENSION DevExt) +{ + NTSTATUS Status; + + Status = I8042BasicDetect(DevExt); + if (Status != STATUS_SUCCESS) { + DPRINT1("Basic keyboard detection failed: %x\n", Status); + return Status; + } + + if (!DevExt->KeyboardExists) { + DPRINT("Keyboard not detected\n") + if (DevExt->Settings.Headless) + /* Act as if it exists regardless */ + DevExt->KeyboardExists = TRUE; + } else { + DPRINT("Keyboard detected\n"); + DevExt->KeyboardExists = I8042DetectKeyboard(DevExt); + } + + if (DevExt->KeyboardExists) { + I8042KeyboardEnable(DevExt); + I8042KeyboardEnableInterrupt(DevExt); + } + + if (DevExt->MouseExists) + I8042MouseEnable(DevExt); + + return STATUS_SUCCESS; +} + +static NTSTATUS STDCALL I8042AddDevice(PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT Pdo) +{ + UNICODE_STRING DeviceName = ROS_STRING_INITIALIZER(L"\Device\KeyboardClass0"); + UNICODE_STRING MouseName = ROS_STRING_INITIALIZER(L"\Device\PointerClass0"); + 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"); + + IoCreateDevice(DriverObject, + sizeof(DEVICE_EXTENSION), + NULL, + FILE_DEVICE_8042_PORT, + FILE_DEVICE_SECURE_OPEN, + TRUE, + &Fdo); + + IoAttachDeviceToDeviceStack(Fdo, Pdo); + + 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); + if (STATUS_SUCCESS != Status) { + 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); + + 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); + } + + 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); + + 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) { + ExFreePool(DevExt->KeyboardBuffer); + DPRINT1("No memory for mouse buffer\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + InsertTailList(&DevExt->BusDevices, &FdoDevExt->BusDevices); + } + + 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, [truncated at 1000 lines; 4079 more skipped]