diff --git a/reactos/drivers/input/i8042prt/README.txt b/reactos/drivers/input/i8042prt/README.txt index a8f09ff5f59..16bb6c791fb 100644 --- a/reactos/drivers/input/i8042prt/README.txt +++ b/reactos/drivers/input/i8042prt/README.txt @@ -7,18 +7,28 @@ not based on the i8042prt example driver that's included with the DDK. The directory contains these files: +createclose.c: open/close devices functionnality + i8042prt.c: Main controller functionality, things shared by keyboards and mice keyboard.c: keyboard functionality: detection, interrupt handling +misc.c: misc things, mostly related to Irp passing + mouse.c: mouse functionality: detection, interrupt handling, packet parsing for standard ps2 and microsoft mice +pnp.c: Plug&Play functionnality + ps2pp.c: logitech ps2++ mouse packat parsing (basic) +readwrite.c: read/write to the i8042 controller + registry.c: registry reading -makefile, i8042prt.rc: obvious +setup.c: add keyboard support during the 1st stage setup + +i8042prt.rc: obvious Some parts of the driver make little sense. This is because it implements @@ -33,7 +43,6 @@ Things to add: - 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: @@ -49,18 +58,6 @@ Things requiring work elsewhere: 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. diff --git a/reactos/drivers/input/i8042prt/createclose.c b/reactos/drivers/input/i8042prt/createclose.c new file mode 100644 index 00000000000..a4bdd8fbeb1 --- /dev/null +++ b/reactos/drivers/input/i8042prt/createclose.c @@ -0,0 +1,52 @@ +/* + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/createclose.c + * PURPOSE: IRP_MJ_CREATE, IRP_MJ_CLEANUP and IRP_MJ_CLOSE operations + * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +/* INCLUDES ******************************************************************/ + +#include "i8042prt.h" + +/* FUNCTIONS *****************************************************************/ + +NTSTATUS NTAPI +i8042Create( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + DPRINT("IRP_MJ_CREATE\n"); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +i8042Cleanup( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + DPRINT("IRP_MJ_CLEANUP\n"); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +i8042Close( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + DPRINT("IRP_MJ_CLOSE\n"); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/input/i8042prt/i8042prt.c b/reactos/drivers/input/i8042prt/i8042prt.c index 2ca0a16a485..ba0dd1bd3b6 100644 --- a/reactos/drivers/input/i8042prt/i8042prt.c +++ b/reactos/drivers/input/i8042prt/i8042prt.c @@ -1,972 +1,543 @@ /* - * 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 + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/i8042prt.c + * PURPOSE: Driver entry function + * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) + Copyright Jason Filby (jasonfilby@yahoo.com) + Copyright Martijn Vernooij (o112w8r02@sneakemail.com) + Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) */ -/* INCLUDES ****************************************************************/ +/* INCLUDES ******************************************************************/ +#define INITGUID #include "i8042prt.h" -#ifndef NDEBUG -#define NDEBUG -#endif -#include - -/* GLOBALS *******************************************************************/ - -/* - * Driver data - */ -#define I8042_TIMEOUT 500000 - -#define I8042_MAX_COMMAND_LENGTH 16 -#define I8042_MAX_UPWARDS_STACK 5 - -UNICODE_STRING I8042RegistryPath; - -static DRIVER_ADD_DEVICE I8042AddDevice; -static NTSTATUS STDCALL I8042AddDevice(PDRIVER_OBJECT DriverObject, - PDEVICE_OBJECT Pdo); - -static DRIVER_STARTIO I8042StartIo; -static VOID STDCALL I8042StartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp); - -static DRIVER_DISPATCH I8042CreateDispatch; -static NTSTATUS STDCALL I8042CreateDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); - -static DRIVER_DISPATCH I8042InternalDeviceControl; -static NTSTATUS STDCALL I8042InternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp); - /* 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; +static DRIVER_STARTIO i8042StartIo; +static DRIVER_DISPATCH IrpStub; +static DRIVER_DISPATCH i8042DeviceControl; +static DRIVER_DISPATCH i8042InternalDeviceControl; +DRIVER_INITIALIZE DriverEntry; - while ((KBD_IBF & READ_PORT_UCHAR(I8042_CTRL_PORT)) && - (ResendIterations--)) +NTSTATUS NTAPI +i8042AddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT Pdo) +{ + PI8042_DRIVER_EXTENSION DriverExtension; + PFDO_DEVICE_EXTENSION DeviceExtension = NULL; + PDEVICE_OBJECT Fdo = NULL; + ULONG DeviceExtensionSize; + NTSTATUS Status; + + DPRINT("i8042AddDevice(%p %p)\n", DriverObject, Pdo); + + DriverExtension = (PI8042_DRIVER_EXTENSION)IoGetDriverObjectExtension(DriverObject, DriverObject); + + if (Pdo == NULL) { - KeStallExecutionProcessor(50); + /* We're getting a NULL Pdo at the first call as + * we are a legacy driver. Ignore it */ + return STATUS_SUCCESS; } - if (ResendIterations) { - WRITE_PORT_UCHAR(addr,data); - DPRINT("Sent %x to %x\n", data, addr); - return TRUE; + /* Create new device object. As we don't know if the device would be a keyboard + * or a mouse, we have to allocate the biggest device extension. */ + DeviceExtensionSize = MAX(sizeof(I8042_KEYBOARD_EXTENSION), sizeof(I8042_MOUSE_EXTENSION)); + Status = IoCreateDevice( + DriverObject, + DeviceExtensionSize, + NULL, + Pdo->DeviceType, + FILE_DEVICE_SECURE_OPEN, + TRUE, + &Fdo); + if (!NT_SUCCESS(Status)) + { + DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status); + goto cleanup; } - 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; + DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension; + RtlZeroMemory(DeviceExtension, DeviceExtensionSize); + DeviceExtension->Type = Unknown; + DeviceExtension->Fdo = Fdo; + DeviceExtension->Pdo = Pdo; + DeviceExtension->PortDeviceExtension = &DriverExtension->Port; + Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice); + if (!NT_SUCCESS(Status)) + { + DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status); + goto cleanup; } - return STATUS_UNSUCCESSFUL; -} -NTSTATUS I8042ReadStatus(UCHAR *Status) -{ - Status[0]=READ_PORT_UCHAR(I8042_CTRL_PORT); + ExInterlockedInsertTailList( + &DriverExtension->DeviceListHead, + &DeviceExtension->ListEntry, + &DriverExtension->DeviceListLock); + + Fdo->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; + +cleanup: + if (DeviceExtension && DeviceExtension->LowerDevice) + IoDetachDevice(DeviceExtension->LowerDevice); + if (Fdo) + IoDeleteDevice(Fdo); + return Status; } -/* - * FUNCTION: Read data from port 0x60 - */ -NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data) +VOID NTAPI +i8042SendHookWorkItem( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) { - ULONG Counter = DevExt->Settings.PollingIterations; + PI8042_HOOK_WORKITEM WorkItemData; + PFDO_DEVICE_EXTENSION FdoDeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + PDEVICE_OBJECT TopOfStack = NULL; + ULONG IoControlCode; + PVOID InputBuffer; + ULONG InputBufferLength; + IO_STATUS_BLOCK IoStatus; + KEVENT Event; + PIRP NewIrp; NTSTATUS Status; - while (Counter--) { - Status = I8042ReadData(Data); + DPRINT("i8042SendHookWorkItem(%p %p)\n", DeviceObject, Context); - if (STATUS_SUCCESS == Status) - return Status; + WorkItemData = (PI8042_HOOK_WORKITEM)Context; + FdoDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + PortDeviceExtension = FdoDeviceExtension->PortDeviceExtension; - 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)) + switch (FdoDeviceExtension->Type) + { + case Keyboard: { - DPRINT1("Failed to write Value\n"); - return STATUS_IO_TIMEOUT; + PI8042_KEYBOARD_EXTENSION DeviceExtension; + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)FdoDeviceExtension; + IoControlCode = IOCTL_INTERNAL_I8042_HOOK_KEYBOARD; + InputBuffer = &DeviceExtension->KeyboardHook; + InputBufferLength = sizeof(INTERNAL_I8042_HOOK_KEYBOARD); + break; } + case Mouse: + { + PI8042_MOUSE_EXTENSION DeviceExtension; + DeviceExtension = (PI8042_MOUSE_EXTENSION)FdoDeviceExtension; + IoControlCode = IOCTL_INTERNAL_I8042_HOOK_MOUSE; + InputBuffer = &DeviceExtension->MouseHook; + InputBufferLength = sizeof(INTERNAL_I8042_HOOK_MOUSE); + break; + } + default: + { + DPRINT1("Unknown FDO type %u\n", FdoDeviceExtension->Type); + ASSERT(FALSE); + WorkItemData->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR; + goto cleanup; + } + } - if (WaitForAck) { - Status = I8042ReadDataWait(DevExt, &Ack); + KeInitializeEvent(&Event, NotificationEvent, FALSE); + TopOfStack = IoGetAttachedDeviceReference(DeviceObject); + + NewIrp = IoBuildDeviceIoControlRequest( + IoControlCode, + TopOfStack, + InputBuffer, + InputBufferLength, + NULL, + 0, + TRUE, + &Event, + &IoStatus); + + if (!NewIrp) + { + DPRINT("IoBuildDeviceIoControlRequest() failed\n"); + WorkItemData->Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + Status = IoCallDriver(TopOfStack, NewIrp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject( + &Event, + Executive, + KernelMode, + FALSE, + NULL); + Status = IoStatus.Status; + } + if (!NT_SUCCESS(Status)) + { + DPRINT("IoCallDriver() failed with status 0x%08lx\n", Status); + goto cleanup; + } + + if (FdoDeviceExtension->Type == Keyboard) + { + PI8042_KEYBOARD_EXTENSION DeviceExtension; + + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)FdoDeviceExtension; + /* Call the hooked initialization if it exists */ + if (DeviceExtension->KeyboardHook.InitializationRoutine) + { + Status = DeviceExtension->KeyboardHook.InitializationRoutine( + DeviceExtension->KeyboardHook.Context, + PortDeviceExtension, + i8042SynchReadPort, + i8042SynchWritePortKbd, + FALSE); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to read Ack\n"); - return Status; + DPRINT("KeyboardHook.InitializationRoutine() failed with status 0x%08lx\n", Status); + WorkItemData->Irp->IoStatus.Status = Status; + goto cleanup; } - if (Ack == KBD_ACK) - return STATUS_SUCCESS; - if (Ack == KBD_RESEND) - DPRINT("I8042 asks for a data resend\n"); - } else { - return STATUS_SUCCESS; } - DPRINT("Reiterating\n"); - ResendIterations--; - } while (ResendIterations); - return STATUS_IO_TIMEOUT; + } +#if 0 + else + { + /* Mouse doesn't have this, but we need to send a + * reset to start the detection. + */ + KIRQL Irql; + + Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); + + i8042Write(PortDeviceExtension, PortDeviceExtension->ControlPort, CTRL_WRITE_MOUSE); + i8042Write(PortDeviceExtension, PortDeviceExtension->DataPort, MOU_CMD_RESET); + + KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); + } +#endif + + WorkItemData->Irp->IoStatus.Status = STATUS_SUCCESS; + +cleanup: + if (TopOfStack != NULL) + ObDereferenceObject(TopOfStack); + WorkItemData->Irp->IoStatus.Information = 0; + IoCompleteRequest(WorkItemData->Irp, IO_NO_INCREMENT); + + IoFreeWorkItem(WorkItemData->WorkItem); + ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); } -/* - * 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) +static VOID NTAPI +i8042StartIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) { - PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)Context; + PFDO_DEVICE_EXTENSION DeviceExtension; - return I8042ReadDataWait(DevExt, Value); + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + switch (DeviceExtension->Type) + { + case Keyboard: + i8042KbdStartIo(DeviceObject, Irp); + break; + default: + DPRINT1("Unknown FDO type %u\n", DeviceExtension->Type); + ASSERT(FALSE); + break; + } } /* Write the current byte of the packet. Returns FALSE in case * of problems. */ -static BOOLEAN STDCALL I8042PacketWrite(PDEVICE_EXTENSION DevExt) +static BOOLEAN +i8042PacketWrite( + IN PPORT_DEVICE_EXTENSION DeviceExtension) { - UCHAR Port = DevExt->PacketPort; + UCHAR Port = DeviceExtension->PacketPort; - if (Port) { - if (!I8042Write(DevExt, - I8042_CTRL_PORT, - Port)) { + if (Port) + { + if (!i8042Write(DeviceExtension, + DeviceExtension->ControlPort, + 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]); + return i8042Write(DeviceExtension, + DeviceExtension->DataPort, + DeviceExtension->Packet.Bytes[DeviceExtension->Packet.CurrentByte]); } +BOOLEAN +i8042PacketIsr( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR Output) +{ + if (DeviceExtension->Packet.State == Idle) + return FALSE; + + switch (Output) + { + case KBD_RESEND: + DeviceExtension->PacketResends++; + if (DeviceExtension->PacketResends > DeviceExtension->Settings.ResendIterations) + { + DeviceExtension->Packet.State = Idle; + DeviceExtension->PacketComplete = TRUE; + DeviceExtension->PacketResult = STATUS_IO_TIMEOUT; + DeviceExtension->PacketResends = 0; + return TRUE; + } + DeviceExtension->Packet.CurrentByte--; + break; + + case KBD_NACK: + DeviceExtension->Packet.State = Idle; + DeviceExtension->PacketComplete = TRUE; + DeviceExtension->PacketResult = STATUS_UNEXPECTED_IO_ERROR; + DeviceExtension->PacketResends = 0; + return TRUE; + + default: + DeviceExtension->PacketResends = 0; + } + + if (DeviceExtension->Packet.CurrentByte >= DeviceExtension->Packet.ByteCount) + { + DeviceExtension->Packet.State = Idle; + DeviceExtension->PacketComplete = TRUE; + DeviceExtension->PacketResult = STATUS_SUCCESS; + return TRUE; + } + + if (!i8042PacketWrite(DeviceExtension)) + { + DeviceExtension->Packet.State = Idle; + DeviceExtension->PacketComplete = TRUE; + DeviceExtension->PacketResult = STATUS_IO_TIMEOUT; + return TRUE; + } + DeviceExtension->Packet.CurrentByte++; + + return TRUE; +} /* * 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) +NTSTATUS +i8042StartPacket( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN PFDO_DEVICE_EXTENSION FdoDeviceExtension, + IN PUCHAR Bytes, + IN ULONG ByteCount, + IN PIRP Irp) { KIRQL Irql; NTSTATUS Status; - PFDO_DEVICE_EXTENSION FdoDevExt = Device->DeviceExtension; - Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt); - DevExt->CurrentIrp = Irp; - DevExt->CurrentIrpDevice = Device; - - if (Idle != DevExt->Packet.State) { + if (DeviceExtension->Packet.State != Idle) + { Status = STATUS_DEVICE_BUSY; - goto startpacketdone; + goto done; } - DevExt->Packet.Bytes = Bytes; - DevExt->Packet.CurrentByte = 0; - DevExt->Packet.ByteCount = ByteCount; - DevExt->Packet.State = SendingBytes; - DevExt->PacketResult = Status = STATUS_PENDING; + switch (FdoDeviceExtension->Type) + { + case Keyboard: DeviceExtension->PacketPort = 0; break; + case Mouse: DeviceExtension->PacketPort = CTRL_WRITE_MOUSE; break; + default: + DPRINT1("Unknown FDO type %u\n", FdoDeviceExtension->Type); + ASSERT(FALSE); + Status = STATUS_INTERNAL_ERROR; + goto done; + } - if (Mouse == FdoDevExt->Type) - DevExt->PacketPort = 0xD4; - else - DevExt->PacketPort = 0; + DeviceExtension->Packet.Bytes = Bytes; + DeviceExtension->Packet.CurrentByte = 0; + DeviceExtension->Packet.ByteCount = ByteCount; + DeviceExtension->Packet.State = SendingBytes; + DeviceExtension->PacketResult = Status = STATUS_PENDING; + DeviceExtension->CurrentIrp = Irp; + DeviceExtension->CurrentIrpDevice = FdoDeviceExtension->Fdo; - if (!I8042PacketWrite(DevExt)) { + if (!i8042PacketWrite(DeviceExtension)) + { Status = STATUS_IO_TIMEOUT; - DevExt->Packet.State = Idle; - DevExt->PacketResult = STATUS_ABANDONED; - goto startpacketdone; + DeviceExtension->Packet.State = Idle; + DeviceExtension->PacketResult = STATUS_ABANDONED; + goto done; } - DevExt->Packet.CurrentByte++; + DeviceExtension->Packet.CurrentByte++; -startpacketdone: - KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql); +done: + KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql); - if (STATUS_PENDING != Status) { - DevExt->CurrentIrp = NULL; - DevExt->CurrentIrpDevice = NULL; + if (Status != STATUS_PENDING) + { + DeviceExtension->CurrentIrp = NULL; + DeviceExtension->CurrentIrpDevice = NULL; Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return Status; } -BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt, - UCHAR Output) +static NTSTATUS NTAPI +IrpStub( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) { - if (Idle == DevExt->Packet.State) - return FALSE; + NTSTATUS Status = Irp->IoStatus.Status; - 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; + /* Do nothing */ + ASSERT(FALSE); IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + +static NTSTATUS NTAPI +i8042DeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PFDO_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + + DPRINT("i8042DeviceControl(%p %p)\n", DeviceObject, Irp); + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + switch (DeviceExtension->Type) + { + case Keyboard: + return i8042KbdDeviceControl(DeviceObject, Irp); + break; + default: + return IrpStub(DeviceObject, Irp); + } return Status; } -static NTSTATUS STDCALL I8042BasicDetect(PDEVICE_EXTENSION DevExt) +static NTSTATUS NTAPI +i8042InternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) { - 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; - UCHAR Value = 0; - - 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); - } - - /* - * Some machines do not reboot if SF is not set. - */ - if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { - DPRINT1("Can't read i8042 mode\n"); - return Status; - } - - Status = I8042ReadDataWait(DevExt, &Value); - if (!NT_SUCCESS(Status)) { - DPRINT1("No response after read i8042 mode\n"); - return Status; - } - - Value |= CCB_SYSTEM_FLAG; - - if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) { - DPRINT1("Can't set i8042 mode\n"); - return Status; - } - - if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) { - DPRINT1("Can't send i8042 mode\n"); - return Status; - } - - 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; + PFDO_DEVICE_EXTENSION DeviceExtension; + ULONG ControlCode; NTSTATUS Status; - InitializeObjectAttributes(&ObjectAttributes, &PathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); - Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes); - if (!NT_SUCCESS(Status)) + DPRINT("i8042InternalDeviceControl(%p %p)\n", DeviceObject, Irp); + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + switch (DeviceExtension->Type) { - DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status); - goto cleanup; + case Unknown: + { + ControlCode = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode; + switch (ControlCode) + { + case IOCTL_INTERNAL_KEYBOARD_CONNECT: + Status = i8042KbdInternalDeviceControl(DeviceObject, Irp); + break; + case IOCTL_INTERNAL_MOUSE_CONNECT: + Status = i8042MouInternalDeviceControl(DeviceObject, Irp); + break; + default: + DPRINT1("Unknown IO control code 0x%lx\n", ControlCode); + ASSERT(FALSE); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + break; + } + case Keyboard: + Status = i8042KbdInternalDeviceControl(DeviceObject, Irp); + break; + case Mouse: + Status = i8042MouInternalDeviceControl(DeviceObject, Irp); + break; + default: + DPRINT1("Unknown FDO type %u\n", DeviceExtension->Type); + ASSERT(FALSE); + Status = STATUS_INTERNAL_ERROR; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + break; } - 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) +NTSTATUS NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath) { - 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; + PI8042_DRIVER_EXTENSION DriverExtension; + ULONG i; NTSTATUS Status; - PDEVICE_EXTENSION DevExt; - PFDO_DEVICE_EXTENSION FdoDevExt; - PDEVICE_OBJECT Fdo; - static BOOLEAN AlreadyInitialized = FALSE; - - 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; - } - - if (AlreadyInitialized) - return STATUS_SUCCESS; - AlreadyInitialized = TRUE; - - Status = IoCreateDevice(DriverObject, - sizeof(DEVICE_EXTENSION), - NULL, - FILE_DEVICE_8042_PORT, - FILE_DEVICE_SECURE_OPEN, - TRUE, - &Fdo); + Status = IoAllocateDriverObjectExtension( + DriverObject, + DriverObject, + sizeof(I8042_DRIVER_EXTENSION), + (PVOID*)&DriverExtension); if (!NT_SUCCESS(Status)) + { + DPRINT("IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status); return Status; + } + RtlZeroMemory(DriverExtension, sizeof(I8042_DRIVER_EXTENSION)); + KeInitializeSpinLock(&DriverExtension->Port.SpinLock); + InitializeListHead(&DriverExtension->DeviceListHead); + KeInitializeSpinLock(&DriverExtension->DeviceListLock); - 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); + Status = DuplicateUnicodeString( + RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, + RegistryPath, + &DriverExtension->RegistryPath); + if (!NT_SUCCESS(Status)) + { + DPRINT("DuplicateUnicodeString() failed with status 0x%08lx\n", Status); return Status; } - if (DevExt->KeyboardExists) { - MappedIrqKeyboard = HalGetInterruptVector(Internal, - 0, - KEYBOARD_IRQ, - 0, - &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; + Status = ReadRegistryEntries(RegistryPath, &DriverExtension->Port.Settings); + if (!NT_SUCCESS(Status)) + { + DPRINT("ReadRegistryEntries() failed with status 0x%08lx\n", Status); + return Status; } - if (DevExt->MouseExists) { - MappedIrqMouse = HalGetInterruptVector(Internal, - 0, - MOUSE_IRQ, - 0, - &DirqlMouse, - &Affinity); + DriverObject->DriverExtension->AddDevice = i8042AddDevice; + DriverObject->DriverStartIo = i8042StartIo; - Status = IoCreateDevice(DriverObject, - sizeof(FDO_DEVICE_EXTENSION), - &MouseName, - FILE_DEVICE_8042_PORT, - FILE_DEVICE_SECURE_OPEN, - TRUE, - &Fdo); + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) + DriverObject->MajorFunction[i] = IrpStub; - if (NT_SUCCESS(Status)) - { - AddRegistryEntry(L"PointerPort", &MouseName, L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\i8042prt"); - FdoDevExt = Fdo->DeviceExtension; + DriverObject->MajorFunction[IRP_MJ_CREATE] = i8042Create; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = i8042Cleanup; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = i8042Close; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = i8042DeviceControl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = i8042InternalDeviceControl; + DriverObject->MajorFunction[IRP_MJ_PNP] = i8042Pnp; - RtlZeroMemory(FdoDevExt, sizeof(FDO_DEVICE_EXTENSION)); + if (IsFirstStageSetup()) + return i8042AddLegacyKeyboard(DriverObject, RegistryPath); - 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, - Latched, - 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, - Latched, - 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; - I8042AddDevice(DriverObject, NULL); - - return(STATUS_SUCCESS); + return STATUS_SUCCESS; } diff --git a/reactos/drivers/input/i8042prt/i8042prt.h b/reactos/drivers/input/i8042prt/i8042prt.h index 733ba8a3cf8..42d4e16f499 100644 --- a/reactos/drivers/input/i8042prt/i8042prt.h +++ b/reactos/drivers/input/i8042prt/i8042prt.h @@ -1,39 +1,50 @@ -#ifndef _I8042DRV_H -#define _I8042DRV_H +#ifndef _I8042PRT_H_ +#define _I8042PRT_H_ -#include +#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 +#include +#include +#include +#include +#include /*----------------------------------------------------- - * DeviceExtension + * Structures * --------------------------------------------------*/ -typedef struct _COMMAND_CONTEXT + +#define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24)) +#define I8042PRT_TAG TAG('8', '0', '4', '2') + +typedef enum { - int NumInput; - int CurInput; - UCHAR * Input; - int NumOutput; - int CurOutput; - UCHAR * Output; - NTSTATUS Status; + dsStopped, + dsStarted, + dsPaused, + dsRemoved, + dsSurpriseRemoved +} DEVICE_STATE; - BOOLEAN GotAck; - KEVENT Event; - - PVOID DevExt; -} COMMAND_CONTEXT, *PCOMMAND_CONTEXT; +typedef struct _I8042_SETTINGS +{ + /* Registry settings */ + ULONG KeyboardDataQueueSize; /* done */ + UNICODE_STRING KeyboardDeviceBaseName; + ULONG MouseDataQueueSize; /* done */ + ULONG MouseResolution; + ULONG MouseSynchIn100ns; + ULONG NumberOfButtons; + UNICODE_STRING PointerDeviceBaseName; + ULONG PollStatusIterations; /* done */ + ULONG OverrideKeyboardType; + ULONG OverrideKeyboardSubtype; + ULONG PollingIterations; /* done */ + ULONG PollingIterationsMaximum; + ULONG ResendIterations; /* done */ + ULONG SampleRate; + ULONG CrashOnCtrlScroll; /* done */ +} I8042_SETTINGS, *PI8042_SETTINGS; typedef enum _MOUSE_TIMEOUT_STATE { @@ -42,108 +53,47 @@ typedef enum _MOUSE_TIMEOUT_STATE TimeoutCancel } MOUSE_TIMEOUT_STATE, *PMOUSE_TIMEOUT_STATE; -/* TODO: part of this should be in the _ATTRIBUTES structs instead */ -typedef struct _I8042_SETTINGS +typedef struct _INTERRUPT_DATA { - 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; + PKINTERRUPT Object; + ULONG Vector; + KIRQL Dirql; + KINTERRUPT_MODE InterruptMode; + BOOLEAN ShareInterrupt; + KAFFINITY Affinity; +} INTERRUPT_DATA, *PINTERRUPT_DATA; -typedef enum _I8042_MOUSE_TYPE +#define WHEEL_DELTA 120 + +struct _I8042_KEYBOARD_EXTENSION; +typedef struct _I8042_KEYBOARD_EXTENSION *PI8042_KEYBOARD_EXTENSION; +struct _I8042_MOUSE_EXTENSION; +typedef struct _I8042_MOUSE_EXTENSION *PI8042_MOUSE_EXTENSION; + +/* PORT_DEVICE_EXTENSION.Flags */ +#define KEYBOARD_PRESENT 0x01 /* A keyboard is attached */ +#define KEYBOARD_CONNECTED 0x02 /* Keyboard received IOCTL_INTERNAL_KEYBOARD_CONNECT */ +#define KEYBOARD_STARTED 0x04 /* Keyboard FDO received IRP_MN_START_DEVICE */ +#define KEYBOARD_INITIALIZED 0x08 /* Keyboard interrupt is connected */ +#define MOUSE_PRESENT 0x10 /* A mouse is attached */ +#define MOUSE_CONNECTED 0x20 /* Mouse received IOCTL_INTERNAL_MOUSE_CONNECT */ +#define MOUSE_STARTED 0x40 /* Mouse FDO received IRP_MN_START_DEVICE */ +#define MOUSE_INITIALIZED 0x80 /* Mouse interrupt is connected */ + +typedef struct _PORT_DEVICE_EXTENSION { - GenericPS2, - Intellimouse, - IntellimouseExplorer, - Ps2pp -} I8042_MOUSE_TYPE, *PI8042_MOUSE_TYPE; + PUCHAR DataPort; /* Usually 0x60 */ + PUCHAR ControlPort; /* Usually 0x64 */ + I8042_SETTINGS Settings; + ULONG Flags; -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; + PI8042_KEYBOARD_EXTENSION KeyboardExtension; + INTERRUPT_DATA KeyboardInterrupt; + PI8042_MOUSE_EXTENSION MouseExtension; + INTERRUPT_DATA MouseInterrupt; 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; + KIRQL HighestDirql; OUTPUT_PACKET Packet; ULONG PacketResends; @@ -154,228 +104,351 @@ typedef struct _DEVICE_EXTENSION PIRP CurrentIrp; PDEVICE_OBJECT CurrentIrpDevice; +} PORT_DEVICE_EXTENSION, *PPORT_DEVICE_EXTENSION; - /* registry config values */ - I8042_SETTINGS Settings; +typedef struct _I8042_DRIVER_EXTENSION +{ + UNICODE_STRING RegistryPath; - /* Debugger stuff */ - BOOLEAN TabPressed; - ULONG DebugKey; - PIO_WORKITEM DebugWorkItem; -} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + PORT_DEVICE_EXTENSION Port; + LIST_ENTRY DeviceListHead; + KSPIN_LOCK DeviceListLock; +} I8042_DRIVER_EXTENSION, *PI8042_DRIVER_EXTENSION; + +typedef enum _I8042_DEVICE_TYPE +{ + Unknown, + Keyboard, + Mouse, + PhysicalDeviceObject +} I8042_DEVICE_TYPE, *PI8042_DEVICE_TYPE; typedef struct _FDO_DEVICE_EXTENSION { - PDEVICE_EXTENSION PortDevExt; I8042_DEVICE_TYPE Type; - PDEVICE_OBJECT DeviceObject; + // Linkage in I8042_DRIVER_EXTENSION.DeviceListHead + LIST_ENTRY ListEntry; + // Associated device object (FDO) + PDEVICE_OBJECT Fdo; + // Associated device object (PDO) + PDEVICE_OBJECT Pdo; + // Lower device object + PDEVICE_OBJECT LowerDevice; + // Current state of the driver + DEVICE_STATE PnpState; - LIST_ENTRY BusDevices; + PPORT_DEVICE_EXTENSION PortDeviceExtension; } FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION; +typedef struct _I8042_KEYBOARD_EXTENSION +{ + FDO_DEVICE_EXTENSION Common; + CONNECT_DATA KeyboardData; + INTERNAL_I8042_HOOK_KEYBOARD KeyboardHook; /* FIXME: IsrWritePort ignored */ + KDPC DpcKeyboard; + + KEYBOARD_INDICATOR_PARAMETERS KeyboardIndicators; + + KEYBOARD_SCAN_STATE KeyboardScanState; + BOOLEAN KeyComplete; + PKEYBOARD_INPUT_DATA KeyboardBuffer; + ULONG KeysInBuffer; + + /* Power keys items */ + ULONG ReportedCaps; + ULONG NewCaps; + ULONG LastPowerKey; + UNICODE_STRING PowerInterfaceName; + PIO_WORKITEM PowerWorkItem; + PIRP PowerIrp; + + /* Debug items */ + ULONG ComboPosition; + PIO_WORKITEM DebugWorkItem; + BOOLEAN TabPressed; +} I8042_KEYBOARD_EXTENSION; + +typedef enum _I8042_MOUSE_TYPE +{ + GenericPS2, + Intellimouse, + IntellimouseExplorer, + Ps2pp +} I8042_MOUSE_TYPE, *PI8042_MOUSE_TYPE; + +typedef struct _I8042_MOUSE_EXTENSION +{ + FDO_DEVICE_EXTENSION Common; + CONNECT_DATA MouseData; + INTERNAL_I8042_HOOK_MOUSE MouseHook; + KDPC DpcMouse; + + MOUSE_ATTRIBUTES MouseAttributes; + + MOUSE_STATE MouseState; + BOOLEAN MouseComplete; + MOUSE_RESET_SUBSTATE MouseResetState; + PMOUSE_INPUT_DATA MouseBuffer; + ULONG MouseInBuffer; + USHORT MouseButtonState; + ULARGE_INTEGER MousePacketStartTime; + + KTIMER TimerMouseTimeout; + KDPC DpcMouseTimeout; + MOUSE_TIMEOUT_STATE MouseTimeoutState; + BOOLEAN MouseTimeoutActive; + + UCHAR MouseLogiBuffer[3]; + I8042_MOUSE_TYPE MouseType; +} I8042_MOUSE_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 MAX(a, b) ((a) >= (b) ? (a) : (b)) -#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) -#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) +#define KEYBOARD_POWER_CODE 0x5E +#define KEYBOARD_SLEEP_CODE 0x5F +#define KEYBOARD_WAKE_CODE 0x63 - -/* - * 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 CTRL_SELF_TEST 0xAA +#define CTRL_WRITE_MOUSE 0xD4 -#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 +#define KBD_CMD_SET_LEDS 0xED +#define KBD_CMD_GET_ID 0xF2 +/*----------------------------------------------------- + * Keyboard responses + * --------------------------------------------------*/ -/* - * 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 MOU_OBF 0x20 #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 commands + * --------------------------------------------------*/ + +#define MOU_CMD_GET_ID 0xF2 +#define MOU_CMD_RESET 0xFF + +/*----------------------------------------------------- * Mouse responses - */ + * --------------------------------------------------*/ + #define MOUSE_ACK 0xFA #define MOUSE_ERROR 0xFC #define MOUSE_NACK 0xFE -/* i8042prt.c */ -extern UNICODE_STRING I8042RegistryPath; +/*----------------------------------------------------- + * Prototypes + * --------------------------------------------------*/ -NTSTATUS I8042ReadData(UCHAR *Data); +/* createclose.c */ -NTSTATUS I8042ReadStatus(UCHAR *Status); +IO_WORKITEM_ROUTINE i8042SendHookWorkItem; -NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data); +DRIVER_DISPATCH i8042Create; -VOID I8042Flush(); +DRIVER_DISPATCH i8042Cleanup; -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); - -IO_WORKITEM_ROUTINE I8042SendHookWorkItem; -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); +DRIVER_DISPATCH i8042Close; /* keyboard.c */ -VOID STDCALL I8042IsrWritePortKbd(PVOID Context, - UCHAR Value); -NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context, - UCHAR Value, - BOOLEAN WaitForAck); +NTSTATUS DDKAPI +i8042SynchWritePortKbd( + IN PVOID Context, + IN UCHAR Value, + IN BOOLEAN WaitForAck); -KSERVICE_ROUTINE I8042InterruptServiceKbd; -BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt, - VOID * Context); +DRIVER_STARTIO i8042KbdStartIo; -VOID STDCALL I8042DpcRoutineKbd(PKDPC Dpc, - PVOID DeferredContext, - PVOID SystemArgument1, - PVOID SystemArgument2); +DRIVER_DISPATCH i8042KbdDeviceControl; -BOOLEAN STDCALL I8042StartIoKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp); +DRIVER_DISPATCH i8042KbdInternalDeviceControl; -NTSTATUS STDCALL I8042InternalDeviceControlKbd(PDEVICE_OBJECT DeviceObject, - PIRP Irp); +KSERVICE_ROUTINE i8042KbdInterruptService; -BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt); +/* i8042prt.c */ -BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt); +DRIVER_ADD_DEVICE i8042AddDevice; -BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt); +BOOLEAN +i8042PacketIsr( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR Output); -/* registry.c */ -VOID STDCALL I8042ReadRegistry(PDRIVER_OBJECT DriverObject, - PDEVICE_EXTENSION DevExt); +NTSTATUS +i8042StartPacket( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN PFDO_DEVICE_EXTENSION FdoDeviceExtension, + IN PUCHAR Bytes, + IN ULONG ByteCount, + IN PIRP Irp); + +/* misc.c */ + +DRIVER_DISPATCH ForwardIrpAndForget; + +DRIVER_DISPATCH ForwardIrpAndWait; + +NTSTATUS +DuplicateUnicodeString( + IN ULONG Flags, + IN PCUNICODE_STRING SourceString, + OUT PUNICODE_STRING DestinationString); /* mouse.c */ -VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc, - PVOID DeferredContext, - PVOID SystemArgument1, - PVOID SystemArgument2); -VOID STDCALL I8042DpcRoutineMouseTimeout(PKDPC Dpc, - PVOID DeferredContext, - PVOID SystemArgument1, - PVOID SystemArgument2); +VOID +i8042MouHandle( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN UCHAR Output); -KSERVICE_ROUTINE I8042InterruptServiceMouse; -BOOLEAN STDCALL I8042InterruptServiceMouse(struct _KINTERRUPT *Interrupt, - VOID *Context); +VOID +i8042MouHandleButtons( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN USHORT Mask); -NTSTATUS STDCALL I8042InternalDeviceControlMouse(PDEVICE_OBJECT DeviceObject, - PIRP Irp); +NTSTATUS +i8042MouInitialize( + IN PI8042_MOUSE_EXTENSION DeviceExtension); -VOID STDCALL I8042QueueMousePacket(PVOID Context); +DRIVER_DISPATCH i8042MouInternalDeviceControl; -VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt, - USHORT Mask); +KSERVICE_ROUTINE i8042MouInterruptService; -VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt, - UCHAR Output); +/* pnp.c */ -BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt); -BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt); -BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt); +BOOLEAN +i8042ChangeMode( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR FlagsToDisable, + IN UCHAR FlagsToEnable); + +DRIVER_DISPATCH i8042Pnp; /* ps2pp.c */ -VOID I8042MouseHandlePs2pp(PDEVICE_EXTENSION DevExt, UCHAR Input); +VOID +i8042MouHandlePs2pp( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN UCHAR Input); -#endif // _KEYBOARD_H_ +/* readwrite.c */ + +VOID +i8042Flush( + IN PPORT_DEVICE_EXTENSION DeviceExtension); + +VOID +i8042IsrWritePort( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR Value, + IN UCHAR SelectCmd OPTIONAL); + +NTSTATUS +i8042ReadData( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR StatusFlags, + OUT PUCHAR Data); +#define i8042ReadKeyboardData(DeviceExtension, Data) \ + i8042ReadData(DeviceExtension, KBD_OBF, Data) +#define i8042ReadMouseData(DeviceExtension, Data) \ + i8042ReadData(DeviceExtension, MOU_OBF, Data) + +NTSTATUS +i8042ReadDataWait( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + OUT PUCHAR Data); + +NTSTATUS +i8042ReadStatus( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + OUT PUCHAR Status); + +NTSTATUS DDKAPI +i8042SynchReadPort( + IN PVOID Context, + OUT PUCHAR Value, + IN BOOLEAN WaitForAck); + +NTSTATUS NTAPI +i8042SynchWritePort( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR Port, + IN UCHAR Value, + IN BOOLEAN WaitForAck); + +BOOLEAN +i8042Write( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN PUCHAR addr, + IN UCHAR data); + +/* registry.c */ + +NTSTATUS +ReadRegistryEntries( + IN PUNICODE_STRING RegistryPath, + OUT PI8042_SETTINGS Settings); + +/* setup.c */ + +BOOLEAN +IsFirstStageSetup( + VOID); + +NTSTATUS +i8042AddLegacyKeyboard( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath); + +#endif // _I8042PRT_H_ diff --git a/reactos/drivers/input/i8042prt/i8042prt.rbuild b/reactos/drivers/input/i8042prt/i8042prt.rbuild index 79cce42bdaf..adacf351270 100644 --- a/reactos/drivers/input/i8042prt/i8042prt.rbuild +++ b/reactos/drivers/input/i8042prt/i8042prt.rbuild @@ -2,13 +2,18 @@ - . + ntoskrnl hal + createclose.c i8042prt.c keyboard.c + misc.c mouse.c + pnp.c ps2pp.c + readwrite.c registry.c + setup.c i8042prt.rc diff --git a/reactos/drivers/input/i8042prt/i8042prt.rc b/reactos/drivers/input/i8042prt/i8042prt.rc index 6c44176265e..eaed77a9427 100644 --- a/reactos/drivers/input/i8042prt/i8042prt.rc +++ b/reactos/drivers/input/i8042prt/i8042prt.rc @@ -1,5 +1,5 @@ #define REACTOS_VERSION_DLL -#define REACTOS_STR_FILE_DESCRIPTION "I8042 Port Device Driver\0" +#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 index ca4b18e60ad..8cfa930605a 100644 --- a/reactos/drivers/input/i8042prt/keyboard.c +++ b/reactos/drivers/input/i8042prt/keyboard.c @@ -1,34 +1,24 @@ /* - * 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 + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/keyboard.c + * PURPOSE: Keyboard specific functions + * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) + Copyright Jason Filby (jasonfilby@yahoo.com) + Copyright Martijn Vernooij (o112w8r02@sneakemail.com) + Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) */ /* INCLUDES ****************************************************************/ #include "i8042prt.h" -#ifdef __REACTOS__ -#include "kdfuncs.h" -#endif - -#ifndef NDEBUG -#define NDEBUG -#endif -#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 }; +static IO_WORKITEM_ROUTINE i8042DebugWorkItem; +static IO_WORKITEM_ROUTINE i8042PowerWorkItem; +/* This structure starts with the same layout as KEYBOARD_INDICATOR_TRANSLATION */ typedef struct _LOCAL_KEYBOARD_INDICATOR_TRANSLATION { USHORT NumberOfIndicatorKeys; INDICATOR_LIST IndicatorList[3]; @@ -39,102 +29,788 @@ static LOCAL_KEYBOARD_INDICATOR_TRANSLATION IndicatorTranslation = { 3, { {0x45, KEYBOARD_NUM_LOCK_ON}, {0x46, KEYBOARD_SCROLL_LOCK_ON}}}; -static IO_WORKITEM_ROUTINE I8042DebugWorkItem; -static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject, - PVOID Context); - /* FUNCTIONS *****************************************************************/ +/* Debug stuff */ +#define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24)) + +static VOID NTAPI +i8042DebugWorkItem( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Key) +{ + DPRINT("Debug key: 0x%02x\n", Key); + + if (!Key) + return; + + /* We hope kernel would understand this. If + * that's not the case, nothing would happen. + */ + KdSystemDebugControl(TAG('R', 'o', 's', ' '), Key, 0, NULL, 0, NULL, KernelMode); +} + /* * These functions are callbacks for filter driver custom interrupt * service routines. */ -VOID STDCALL I8042IsrWritePortKbd(PVOID Context, - UCHAR Value) +/*static VOID NTAPI +i8042KbdIsrWritePort( + IN PVOID Context, + IN UCHAR Value) { - I8042IsrWritePort(Context, Value, 0); -} + PI8042_KEYBOARD_EXTENSION DeviceExtension; -static VOID STDCALL I8042QueueKeyboardPacket(PVOID Context) + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context; + + if (DeviceExtension->KeyboardHook.IsrWritePort) + { + DeviceExtension->KeyboardHook.IsrWritePort( + DeviceExtension->KeyboardHook.CallContext, + Value); + } + else + i8042IsrWritePort(Context, Value, 0); +}*/ + +static VOID NTAPI +i8042KbdQueuePacket( + IN PVOID Context) { - PDEVICE_OBJECT DeviceObject = Context; - PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension; - PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt; + PI8042_KEYBOARD_EXTENSION DeviceExtension; - DevExt->KeyComplete = TRUE; - DevExt->KeysInBuffer++; - if (DevExt->KeysInBuffer > - DevExt->KeyboardAttributes.InputDataQueueLength) { + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context; + + DeviceExtension->KeyComplete = TRUE; + DeviceExtension->KeysInBuffer++; + if (DeviceExtension->KeysInBuffer > DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize) + { DPRINT1("Keyboard buffer overflow\n"); - DevExt->KeysInBuffer--; + DeviceExtension->KeysInBuffer--; } DPRINT("Irq completes key\n"); - KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL); + KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL); } /* * These functions are callbacks for filter driver custom * initialization routines. */ -NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context, - UCHAR Value, - BOOLEAN WaitForAck) +NTSTATUS DDKAPI +i8042SynchWritePortKbd( + IN PVOID Context, + IN UCHAR Value, + IN BOOLEAN WaitForAck) { - return I8042SynchWritePort((PDEVICE_EXTENSION)Context, - 0, - Value, - WaitForAck); + return i8042SynchWritePort( + (PPORT_DEVICE_EXTENSION)Context, + 0, + Value, + WaitForAck); } -BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt, - VOID * Context) +/* + * Process the keyboard internal device requests + */ +VOID NTAPI +i8042KbdStartIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) { - UCHAR Output; - UCHAR PortStatus; + PIO_STACK_LOCATION Stack; + PI8042_KEYBOARD_EXTENSION DeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + + Stack = IoGetCurrentIrpStackLocation(Irp); + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension; + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + + switch (Stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_KEYBOARD_SET_INDICATORS: + { + DPRINT("IOCTL_KEYBOARD_SET_INDICATORS\n"); + + PortDeviceExtension->PacketBuffer[0] = KBD_CMD_SET_LEDS; + PortDeviceExtension->PacketBuffer[1] = 0; + if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON) + PortDeviceExtension->PacketBuffer[1] |= KBD_LED_CAPS; + + if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON) + PortDeviceExtension->PacketBuffer[1] |= KBD_LED_NUM; + + if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON) + PortDeviceExtension->PacketBuffer[1] |= KBD_LED_SCROLL; + + i8042StartPacket( + PortDeviceExtension, + &DeviceExtension->Common, + PortDeviceExtension->PacketBuffer, + 2, + Irp); + break; + } + default: + { + DPRINT("Unknown ioctl code 0x%lx\n", + Stack->Parameters.DeviceIoControl.IoControlCode); + ASSERT(FALSE); + } + } +} + +static VOID +i8042PacketDpc( + IN PPORT_DEVICE_EXTENSION DeviceExtension) +{ + BOOLEAN FinishIrp = FALSE; + KIRQL Irql; + NTSTATUS Result = STATUS_INTERNAL_ERROR; /* Shouldn't happen */ + + /* If the interrupt happens before this is setup, the key + * was already in the buffer. Too bad! */ + if (!DeviceExtension->HighestDIRQLInterrupt) + return; + + Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt); + + if (DeviceExtension->Packet.State == Idle + && DeviceExtension->PacketComplete) + { + FinishIrp = TRUE; + Result = DeviceExtension->PacketResult; + DeviceExtension->PacketComplete = FALSE; + } + + KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql); + + if (!FinishIrp) + return; + + if (DeviceExtension->CurrentIrp) + { + DeviceExtension->CurrentIrp->IoStatus.Status = Result; + IoCompleteRequest(DeviceExtension->CurrentIrp, IO_NO_INCREMENT); + IoStartNextPacket(DeviceExtension->CurrentIrpDevice, FALSE); + DeviceExtension->CurrentIrp = NULL; + DeviceExtension->CurrentIrpDevice = NULL; + } +} + +static VOID NTAPI +i8042PowerWorkItem( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) +{ + PI8042_KEYBOARD_EXTENSION DeviceExtension; + PIRP WaitingIrp; NTSTATUS Status; - PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context; - BOOLEAN HookContinue = FALSE, HookReturn; - ULONG Iterations = 0; - KEYBOARD_INPUT_DATA *InputData = - DevExt->KeyboardBuffer + DevExt->KeysInBuffer; + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context; - do { - Status = I8042ReadStatus(&PortStatus); - DPRINT("PortStatus: %x\n", PortStatus); - Status = I8042ReadData(&Output); - Iterations++; - if (STATUS_SUCCESS == Status) + /* See http://blogs.msdn.com/doronh/archive/2006/09/08/746961.aspx */ + + /* Register GUID_DEVICE_SYS_BUTTON interface and report capability */ + if (DeviceExtension->NewCaps != DeviceExtension->ReportedCaps) + { + WaitingIrp = InterlockedExchangePointer(&DeviceExtension->PowerIrp, NULL); + if (WaitingIrp) + { + /* Cancel the current power irp, as capability changed */ + WaitingIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; + WaitingIrp->IoStatus.Information = sizeof(ULONG); + IoCompleteRequest(WaitingIrp, IO_NO_INCREMENT); + } + + if (DeviceExtension->PowerInterfaceName.MaximumLength == 0) + { + /* We have never registred this interface ; do it */ + Status = IoRegisterDeviceInterface( + DeviceExtension->Common.Pdo, + &GUID_DEVICE_SYS_BUTTON, + NULL, + &DeviceExtension->PowerInterfaceName); + if (!NT_SUCCESS(Status)) + { + /* We can't do more yet, ignore the keypress... */ + DPRINT("IoRegisterDeviceInterface(GUID_DEVICE_SYS_BUTTON) failed with status 0x%08lx\n", + Status); + DeviceExtension->PowerInterfaceName.MaximumLength = 0; + return; + } + } + else + { + /* Disable the interface. Once activated again, capabilities would be asked again */ + Status = IoSetDeviceInterfaceState( + &DeviceExtension->PowerInterfaceName, + FALSE); + if (!NT_SUCCESS(Status)) + { + /* Ignore the key press... */ + DPRINT("Disabling interface %wZ failed with status 0x%08lx\n", + &DeviceExtension->PowerInterfaceName, Status); + return; + } + } + /* Enable the interface. This leads to receving a IOCTL_GET_SYS_BUTTON_CAPS, + * so we can report new capability */ + Status = IoSetDeviceInterfaceState( + &DeviceExtension->PowerInterfaceName, + TRUE); + if (!NT_SUCCESS(Status)) + { + /* Ignore the key press... */ + DPRINT("Enabling interface %wZ failed with status 0x%08lx\n", + &DeviceExtension->PowerInterfaceName, Status); + return; + } + } + + /* Directly complete the IOCTL_GET_SYS_BUTTON_EVENT Irp (if any) */ + WaitingIrp = InterlockedExchangePointer(&DeviceExtension->PowerIrp, NULL); + if (WaitingIrp) + { + PULONG pEvent = (PULONG)WaitingIrp->AssociatedIrp.SystemBuffer; + + WaitingIrp->IoStatus.Status = STATUS_SUCCESS; + WaitingIrp->IoStatus.Information = sizeof(ULONG); + *pEvent = InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, 0); + IoCompleteRequest(WaitingIrp, IO_NO_INCREMENT); + } +} + +/* Return TRUE if it was a power key */ +static BOOLEAN +HandlePowerKeys( + IN PI8042_KEYBOARD_EXTENSION DeviceExtension) +{ + PKEYBOARD_INPUT_DATA InputData; + ULONG KeyPress; + + InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer - 1; + if (!(InputData->Flags & KEY_E0)) + return FALSE; + + if (InputData->Flags & KEY_BREAK) + /* We already took care of the key press */ + return TRUE; + + switch (InputData->MakeCode) + { + case KEYBOARD_POWER_CODE: + KeyPress = SYS_BUTTON_POWER; + break; + case KEYBOARD_SLEEP_CODE: + KeyPress = SYS_BUTTON_SLEEP; + break; + case KEYBOARD_WAKE_CODE: + KeyPress = SYS_BUTTON_WAKE; + break; + default: + return FALSE; + } + + /* Our work can only be done at passive level, so use a workitem */ + DeviceExtension->NewCaps |= KeyPress; + InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, KeyPress); + IoQueueWorkItem( + DeviceExtension->PowerWorkItem, + &i8042PowerWorkItem, + DelayedWorkQueue, + DeviceExtension); + return TRUE; +} + +static VOID NTAPI +i8042KbdDpcRoutine( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + PI8042_KEYBOARD_EXTENSION DeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + ULONG KeysTransferred = 0; + ULONG KeysInBufferCopy; + KIRQL Irql; + + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeferredContext; + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + + if (HandlePowerKeys(DeviceExtension)) + { + DeviceExtension->KeyComplete = FALSE; + return; + } + + i8042PacketDpc(PortDeviceExtension); + if (!DeviceExtension->KeyComplete) + return; + /* We got the interrupt as it was being enabled, too bad */ + if (!PortDeviceExtension->HighestDIRQLInterrupt) + return; + + Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); + + DeviceExtension->KeyComplete = FALSE; + KeysInBufferCopy = DeviceExtension->KeysInBuffer; + + KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); + + if (PortDeviceExtension->Settings.CrashOnCtrlScroll) + { + PKEYBOARD_INPUT_DATA InputData; + InputData = DeviceExtension->KeyboardBuffer + KeysInBufferCopy - 1; + + /* Test for TAB + key combination */ + if (InputData->MakeCode == 0x0F) + DeviceExtension->TabPressed = !(InputData->Flags & KEY_BREAK); + else if (DeviceExtension->TabPressed) + { + DeviceExtension->TabPressed = FALSE; + + IoQueueWorkItem( + DeviceExtension->DebugWorkItem, + &i8042DebugWorkItem, + DelayedWorkQueue, + (PVOID)(ULONG_PTR)InputData->MakeCode); + } + } + + DPRINT("Send a key\n"); + + if (!DeviceExtension->KeyboardData.ClassService) + return; + + DPRINT("Sending %lu key(s)\n", KeysInBufferCopy); + (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->KeyboardData.ClassService)( + DeviceExtension->KeyboardData.ClassDeviceObject, + DeviceExtension->KeyboardBuffer, + DeviceExtension->KeyboardBuffer + KeysInBufferCopy, + &KeysTransferred); + + KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); + DeviceExtension->KeysInBuffer -= KeysTransferred; + KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); +} + +/* + * Runs the keyboard IOCTL dispatch. + */ +NTSTATUS NTAPI +i8042KbdDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + PI8042_KEYBOARD_EXTENSION DeviceExtension; + NTSTATUS Status; + + Stack = IoGetCurrentIrpStackLocation(Irp); + Irp->IoStatus.Information = 0; + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension; + + switch (Stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_GET_SYS_BUTTON_CAPS: + { + /* Part of GUID_DEVICE_SYS_BUTTON interface */ + PULONG pCaps; + DPRINT("IOCTL_GET_SYS_BUTTON_CAPS\n"); + + if (Stack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG)) + Status = STATUS_INVALID_PARAMETER; + else + { + pCaps = (PULONG)Irp->AssociatedIrp.SystemBuffer; + *pCaps = DeviceExtension->NewCaps; + DeviceExtension->ReportedCaps = DeviceExtension->NewCaps; + Irp->IoStatus.Information = sizeof(ULONG); + Status = STATUS_SUCCESS; + } + break; + } + case IOCTL_GET_SYS_BUTTON_EVENT: + { + /* Part of GUID_DEVICE_SYS_BUTTON interface */ + PIRP WaitingIrp; + DPRINT("IOCTL_GET_SYS_BUTTON_EVENT\n"); + + if (Stack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG)) + Status = STATUS_INVALID_PARAMETER; + else + { + WaitingIrp = InterlockedCompareExchangePointer( + &DeviceExtension->PowerIrp, + Irp, + NULL); + /* Check if an Irp is already pending */ + if (WaitingIrp) + { + /* Unable to have a 2nd pending IRP for this IOCTL */ + DPRINT1("Unable to pend a second IRP for IOCTL_GET_SYS_BUTTON_EVENT\n"); + Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + else + { + ULONG PowerKey; + PowerKey = InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, 0); + if (PowerKey != 0) + { + (VOID)InterlockedCompareExchangePointer(&DeviceExtension->PowerIrp, NULL, Irp); + *(PULONG)Irp->AssociatedIrp.SystemBuffer = PowerKey; + Status = STATUS_SUCCESS; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = sizeof(ULONG); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + else + { + DPRINT("Pending IOCTL_GET_SYS_BUTTON_EVENT\n"); + Status = STATUS_PENDING; + Irp->IoStatus.Status = Status; + IoMarkIrpPending(Irp); + } + } + return Status; + } + break; + } + default: + { + DPRINT("IRP_MJ_DEVICE_CONTROL / unknown ioctl code 0x%lx\n", + Stack->Parameters.DeviceIoControl.IoControlCode); + ASSERT(FALSE); + return ForwardIrpAndForget(DeviceObject, Irp); + } + } + + Irp->IoStatus.Status = Status; + if (Status == STATUS_PENDING) + IoMarkIrpPending(Irp); + else + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +/* + * Runs the keyboard IOCTL_INTERNAL dispatch. + */ +NTSTATUS NTAPI +i8042KbdInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + PI8042_KEYBOARD_EXTENSION DeviceExtension; + NTSTATUS Status; + + Stack = IoGetCurrentIrpStackLocation(Irp); + Irp->IoStatus.Information = 0; + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension; + + switch (Stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_INTERNAL_KEYBOARD_CONNECT: + { + SIZE_T Size; + PIO_WORKITEM WorkItem = NULL; + PI8042_HOOK_WORKITEM WorkItemData = NULL; + + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_CONNECT\n"); + if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA)) + { + Status = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + DeviceExtension->KeyboardData = + *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer); + + /* Send IOCTL_INTERNAL_I8042_HOOK_KEYBOARD to device stack */ + WorkItem = IoAllocateWorkItem(DeviceObject); + if (!WorkItem) + { + DPRINT("IoAllocateWorkItem() failed\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + WorkItemData = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(I8042_HOOK_WORKITEM), + I8042PRT_TAG); + if (!WorkItemData) + { + DPRINT("ExAllocatePoolWithTag() failed\n"); + Status = STATUS_NO_MEMORY; + goto cleanup; + } + WorkItemData->WorkItem = WorkItem; + WorkItemData->Irp = Irp; + + /* Initialize extension */ + DeviceExtension->Common.Type = Keyboard; + Size = DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize * sizeof(KEYBOARD_INPUT_DATA); + DeviceExtension->KeyboardBuffer = ExAllocatePoolWithTag( + NonPagedPool, + Size, + I8042PRT_TAG); + if (!DeviceExtension->KeyboardBuffer) + { + DPRINT("ExAllocatePoolWithTag() failed\n"); + Status = STATUS_NO_MEMORY; + goto cleanup; + } + RtlZeroMemory(DeviceExtension->KeyboardBuffer, Size); + KeInitializeDpc( + &DeviceExtension->DpcKeyboard, + i8042KbdDpcRoutine, + DeviceExtension); + DeviceExtension->PowerWorkItem = IoAllocateWorkItem(DeviceObject); + if (!DeviceExtension->PowerWorkItem) + { + DPRINT("IoAllocateWorkItem() failed\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + DeviceExtension->DebugWorkItem = IoAllocateWorkItem(DeviceObject); + if (!DeviceExtension->DebugWorkItem) + { + DPRINT("IoAllocateWorkItem() failed\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + DeviceExtension->Common.PortDeviceExtension->KeyboardExtension = DeviceExtension; + DeviceExtension->Common.PortDeviceExtension->Flags |= KEYBOARD_CONNECTED; + + IoMarkIrpPending(Irp); + /* FIXME: DeviceExtension->KeyboardHook.IsrWritePort = ; */ + DeviceExtension->KeyboardHook.QueueKeyboardPacket = i8042KbdQueuePacket; + DeviceExtension->KeyboardHook.CallContext = DeviceExtension; + IoQueueWorkItem(WorkItem, + i8042SendHookWorkItem, + DelayedWorkQueue, + WorkItemData); + Status = STATUS_PENDING; + break; + +cleanup: + if (DeviceExtension->KeyboardBuffer) + ExFreePoolWithTag(DeviceExtension->KeyboardBuffer, I8042PRT_TAG); + if (DeviceExtension->PowerWorkItem) + IoFreeWorkItem(DeviceExtension->PowerWorkItem); + if (DeviceExtension->DebugWorkItem) + IoFreeWorkItem(DeviceExtension->DebugWorkItem); + if (WorkItem) + IoFreeWorkItem(WorkItem); + if (WorkItemData) + ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); + break; + } + case IOCTL_INTERNAL_KEYBOARD_DISCONNECT: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_DISCONNECT\n"); + /* MSDN says that operation is to implemented. + * To implement it, we just have to do: + * DeviceExtension->KeyboardData.ClassService = NULL; + */ + Status = STATUS_NOT_IMPLEMENTED; + break; + } + case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_KEYBOARD\n"); + /* Nothing to do here */ + Status = STATUS_SUCCESS; + break; + } + case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION\n"); + + /* We should check the UnitID, but it's kind of pointless as + * all keyboards are supposed to have the same one + */ + if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + RtlCopyMemory( + Irp->AssociatedIrp.SystemBuffer, + &IndicatorTranslation, + sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)); + Irp->IoStatus.Information = sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION); + Status = STATUS_SUCCESS; + } + break; + } + case IOCTL_KEYBOARD_QUERY_INDICATORS: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATORS\n"); + + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + RtlCopyMemory( + Irp->AssociatedIrp.SystemBuffer, + &DeviceExtension->KeyboardIndicators, + sizeof(KEYBOARD_INDICATOR_PARAMETERS)); + Irp->IoStatus.Information = sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION); + Status = STATUS_SUCCESS; + } + break; + } + case IOCTL_KEYBOARD_SET_INDICATORS: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_SET_INDICATORS\n"); + + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + RtlCopyMemory( + &DeviceExtension->KeyboardIndicators, + Irp->AssociatedIrp.SystemBuffer, + sizeof(KEYBOARD_INDICATOR_PARAMETERS)); + Status = STATUS_PENDING; + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + } + break; + } + default: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n", + Stack->Parameters.DeviceIoControl.IoControlCode); + ASSERT(FALSE); + return ForwardIrpAndForget(DeviceObject, Irp); + } + } + + Irp->IoStatus.Status = Status; + if (Status != STATUS_PENDING) + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + +/* + * Call the customization hook. The ToReturn 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 +i8042KbdCallIsrHook( + IN PI8042_KEYBOARD_EXTENSION DeviceExtension, + IN UCHAR Status, + IN UCHAR Input, + OUT PBOOLEAN ToReturn) +{ + BOOLEAN HookReturn, HookContinue; + + HookContinue = FALSE; + + if (DeviceExtension->KeyboardHook.IsrRoutine) + { + HookReturn = DeviceExtension->KeyboardHook.IsrRoutine( + DeviceExtension->KeyboardHook.Context, + DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer, + &DeviceExtension->Common.PortDeviceExtension->Packet, + Status, + &Input, + &HookContinue, + &DeviceExtension->KeyboardScanState); + + if (!HookContinue) + { + *ToReturn = HookReturn; + return TRUE; + } + } + return FALSE; +} + +BOOLEAN NTAPI +i8042KbdInterruptService( + IN PKINTERRUPT Interrupt, + PVOID Context) +{ + PI8042_KEYBOARD_EXTENSION DeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + PKEYBOARD_INPUT_DATA InputData; + ULONG Counter; + UCHAR PortStatus, Output; + BOOLEAN ToReturn = FALSE; + NTSTATUS Status; + + DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context; + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer; + Counter = PortDeviceExtension->Settings.PollStatusIterations; + + while (Counter) + { + Status = i8042ReadStatus(PortDeviceExtension, &PortStatus); + if (!NT_SUCCESS(Status)) + { + DPRINT("i8042ReadStatus() failed with status 0x%08lx\n", Status); + return FALSE; + } + Status = i8042ReadKeyboardData(PortDeviceExtension, &Output); + if (NT_SUCCESS(Status)) break; KeStallExecutionProcessor(1); - } while (Iterations < DevExt->Settings.PollStatusIterations); - - if (STATUS_SUCCESS != Status) { - DPRINT("Spurious I8042 interrupt\n"); + Counter--; + } + if (Counter == 0) + { + DPRINT("Spurious i8042 keyboard interrupt\n"); return FALSE; } - DPRINT("Got: %x\n", Output); + DPRINT("Got: 0x%02x\n", Output); - if (DevExt->KeyboardHook.IsrRoutine) { - HookReturn = DevExt->KeyboardHook.IsrRoutine( - DevExt->KeyboardHook.Context, - InputData, - &DevExt->Packet, - PortStatus, - &Output, - &HookContinue, - &DevExt->KeyboardScanState); + if (PortDeviceExtension->Settings.CrashOnCtrlScroll) + { + /* Test for CTRL + SCROLL LOCK twice */ + static const UCHAR ScanCodes[] = { 0x1d, 0x46, 0x46, 0 }; - if (!HookContinue) - return HookReturn; + if (!(Output & 0x80)) + { + if ((Output & 0x7f) == ScanCodes[DeviceExtension->ComboPosition]) + { + DeviceExtension->ComboPosition++; + if (ScanCodes[DeviceExtension->ComboPosition] == 0) + KeBugCheck(MANUALLY_INITIATED_CRASH); + } + else if ((Output & 0x7f) == ScanCodes[0]) + DeviceExtension->ComboPosition = 1; + else + DeviceExtension->ComboPosition = 0; + } } - if (I8042PacketIsr(DevExt, Output)) { - if (DevExt->PacketComplete) { + if (i8042KbdCallIsrHook(DeviceExtension, PortStatus, Output, &ToReturn)) + return ToReturn; + + if (i8042PacketIsr(PortDeviceExtension, Output)) + { + if (PortDeviceExtension->PacketComplete) + { DPRINT("Packet complete\n"); - KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL); + KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL); } DPRINT("Irq eaten by packet\n"); return TRUE; @@ -142,638 +818,43 @@ BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt, 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 */ + if (DeviceExtension->KeyboardScanState == Normal) + { + switch (Output) + { + case 0xe0: + DeviceExtension->KeyboardScanState = GotE0; + return TRUE; + case 0xe1: + DeviceExtension->KeyboardScanState = GotE1; + return TRUE; + default: + break; } } + /* Update InputData */ InputData->Flags = 0; - - switch (DevExt->KeyboardScanState) { - case GotE0: - InputData->Flags |= KEY_E0; - break; - case GotE1: - InputData->Flags |= KEY_E1; - break; - default: - ; + switch (DeviceExtension->KeyboardScanState) + { + case GotE0: + InputData->Flags |= KEY_E0; + break; + case GotE1: + InputData->Flags |= KEY_E1; + break; + default: + break; } - DevExt->KeyboardScanState = Normal; - + DeviceExtension->KeyboardScanState = Normal; if (Output & 0x80) InputData->Flags |= KEY_BREAK; else InputData->Flags |= KEY_MAKE; - InputData->MakeCode = Output & 0x7f; + InputData->Reserved = 0; - I8042QueueKeyboardPacket(DevExt->KeyboardObject); + DeviceExtension->KeyboardHook.QueueKeyboardPacket(DeviceExtension->KeyboardHook.CallContext); 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 */ -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. - */ - KdSystemDebugControl(TAG('R', 'o', 's', ' '), (PVOID)Key, 0, NULL, 0, NULL, KernelMode); -#endif /* __REACTOS__ */ -} diff --git a/reactos/drivers/input/i8042prt/misc.c b/reactos/drivers/input/i8042prt/misc.c new file mode 100644 index 00000000000..74981aca204 --- /dev/null +++ b/reactos/drivers/input/i8042prt/misc.c @@ -0,0 +1,112 @@ +/* + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/misc.c + * PURPOSE: Misceallenous operations + * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +/* INCLUDES ******************************************************************/ + +#include "i8042prt.h" + +/* FUNCTIONS *****************************************************************/ + +static IO_COMPLETION_ROUTINE ForwardIrpAndWaitCompletion; + +static NTSTATUS NTAPI +ForwardIrpAndWaitCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + if (Irp->PendingReturned) + KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS NTAPI +ForwardIrpAndWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_OBJECT LowerDevice = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerDevice; + KEVENT Event; + NTSTATUS Status; + + ASSERT(LowerDevice); + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + IoCopyCurrentIrpStackLocationToNext(Irp); + + DPRINT("Calling lower device %p\n", LowerDevice); + IoSetCompletionRoutine(Irp, ForwardIrpAndWaitCompletion, &Event, TRUE, TRUE, TRUE); + + Status = IoCallDriver(LowerDevice, Irp); + if (Status == STATUS_PENDING) + { + Status = KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); + if (NT_SUCCESS(Status)) + Status = Irp->IoStatus.Status; + } + + return Status; +} + +NTSTATUS NTAPI +ForwardIrpAndForget( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_OBJECT LowerDevice = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerDevice; + + ASSERT(LowerDevice); + + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(LowerDevice, Irp); +} + +NTSTATUS +DuplicateUnicodeString( + IN ULONG Flags, + IN PCUNICODE_STRING SourceString, + OUT PUNICODE_STRING DestinationString) +{ + if (SourceString == NULL || DestinationString == NULL + || SourceString->Length > SourceString->MaximumLength + || (SourceString->Length == 0 && SourceString->MaximumLength > 0 && SourceString->Buffer == NULL) + || Flags == RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING || Flags >= 4) + { + return STATUS_INVALID_PARAMETER; + } + + + if ((SourceString->Length == 0) + && (Flags != (RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE | + RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING))) + { + DestinationString->Length = 0; + DestinationString->MaximumLength = 0; + DestinationString->Buffer = NULL; + } + else + { + USHORT DestMaxLength = SourceString->Length; + + if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE) + DestMaxLength += sizeof(UNICODE_NULL); + + DestinationString->Buffer = ExAllocatePoolWithTag(PagedPool, DestMaxLength, I8042PRT_TAG); + if (DestinationString->Buffer == NULL) + return STATUS_NO_MEMORY; + + RtlCopyMemory(DestinationString->Buffer, SourceString->Buffer, SourceString->Length); + DestinationString->Length = SourceString->Length; + DestinationString->MaximumLength = DestMaxLength; + + if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE) + DestinationString->Buffer[DestinationString->Length / sizeof(WCHAR)] = 0; + } + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/input/i8042prt/mouse.c b/reactos/drivers/input/i8042prt/mouse.c index 7fdf21bc632..40e1f4912ba 100644 --- a/reactos/drivers/input/i8042prt/mouse.c +++ b/reactos/drivers/input/i8042prt/mouse.c @@ -1,44 +1,484 @@ /* - * 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 + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/mouse.c + * PURPOSE: Mouse specific functions + * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) + Copyright Jason Filby (jasonfilby@yahoo.com) + Copyright Martijn Vernooij (o112w8r02@sneakemail.com) + Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) */ /* INCLUDES ****************************************************************/ #include "i8042prt.h" -#ifndef NDEBUG -#define NDEBUG -#endif -#include +/* FUNCTIONS *****************************************************************/ /* * These functions are callbacks for filter driver custom interrupt * service routines. */ -static VOID STDCALL I8042IsrWritePortMouse(PVOID Context, - UCHAR Value) +static VOID NTAPI +i8042MouIsrWritePort( + IN PVOID Context, + IN UCHAR Value) { - I8042IsrWritePort(Context, Value, 0xD4); + PI8042_MOUSE_EXTENSION DeviceExtension; + + DeviceExtension = (PI8042_MOUSE_EXTENSION)Context; + + if (DeviceExtension->MouseHook.IsrWritePort) + { + DeviceExtension->MouseHook.IsrWritePort( + DeviceExtension->MouseHook.CallContext, + Value); + } + else + i8042IsrWritePort(Context, Value, CTRL_WRITE_MOUSE); } -#if 0 -static NTSTATUS STDCALL I8042SynchWritePortMouse(PVOID Context, - UCHAR Value, - BOOLEAN WaitForAck) +static VOID NTAPI +i8042MouQueuePacket( + IN PVOID Context) { - return I8042SynchWritePort((PDEVICE_EXTENSION)Context, - 0xD4, - Value, - WaitForAck); + PI8042_MOUSE_EXTENSION DeviceExtension; + + DeviceExtension = (PI8042_MOUSE_EXTENSION)Context; + + DeviceExtension->MouseComplete = TRUE; + DeviceExtension->MouseInBuffer++; + if (DeviceExtension->MouseInBuffer > DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize) + { + DPRINT1("Mouse buffer overflow\n"); + DeviceExtension->MouseInBuffer--; + } + + DPRINT("Irq completes mouse packet\n"); + KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL); +} + +VOID +i8042MouHandle( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN UCHAR Output) +{ + PMOUSE_INPUT_DATA MouseInput; + CHAR Scroll; + + MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer; + + switch (DeviceExtension->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; + + DeviceExtension->MouseState = XMovement; + break; + + case XMovement: + if (MouseInput->LastX) + MouseInput->LastX = (LONG) Output - 256; + else + MouseInput->LastX = Output; + + DeviceExtension->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 (DeviceExtension->MouseType == GenericPS2 || + DeviceExtension->MouseType == Ps2pp) + { + i8042MouHandleButtons( + DeviceExtension, + MOUSE_LEFT_BUTTON_DOWN | + MOUSE_RIGHT_BUTTON_DOWN | + MOUSE_MIDDLE_BUTTON_DOWN); + DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext); + DeviceExtension->MouseState = MouseIdle; + } + else + { + DeviceExtension->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 (DeviceExtension->MouseType == IntellimouseExplorer) + { + if (Output & 16) + MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN; + if (Output & 32) + MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN; + } + i8042MouHandleButtons( + DeviceExtension, + MOUSE_LEFT_BUTTON_DOWN | + MOUSE_RIGHT_BUTTON_DOWN | + MOUSE_MIDDLE_BUTTON_DOWN | + MOUSE_BUTTON_4_DOWN | + MOUSE_BUTTON_5_DOWN); + DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext); + DeviceExtension->MouseState = MouseIdle; + break; + + default: + DPRINT1("Unexpected state 0x%u!\n", DeviceExtension->MouseState); + ASSERT(FALSE); + } +} + +/* + * Updates ButtonFlags according to RawButtons and a saved state; + * Only takes in account the bits that are set in Mask + */ +VOID +i8042MouHandleButtons( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN USHORT Mask) +{ + PMOUSE_INPUT_DATA MouseInput; + USHORT NewButtonData; + USHORT ButtonDiff; + + MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer; + NewButtonData = (USHORT)(MouseInput->RawButtons & Mask); + ButtonDiff = (NewButtonData ^ DeviceExtension->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: %u/%u/%u\n", + MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN, + MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN, + MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP); + + DeviceExtension->MouseButtonState = + (DeviceExtension->MouseButtonState & ~Mask) | (NewButtonData & Mask); +} + +static NTSTATUS OldInitialization(PPORT_DEVICE_EXTENSION); /* FIXME */ + +/* Does lastest initializations for the mouse. This method + * is called just before connecting the interrupt. + */ +NTSTATUS +i8042MouInitialize( + IN PI8042_MOUSE_EXTENSION DeviceExtension) +{ + PPORT_DEVICE_EXTENSION PortDeviceExtension; + + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + +/* FIXME */ OldInitialization(PortDeviceExtension); + + return STATUS_SUCCESS; +} + +static VOID NTAPI +i8042MouDpcRoutine( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + PI8042_MOUSE_EXTENSION DeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + ULONG MouseTransferred = 0; + ULONG MouseInBufferCopy; + KIRQL Irql; + LARGE_INTEGER Timeout; + + DeviceExtension = (PI8042_MOUSE_EXTENSION)DeferredContext; + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + + switch (DeviceExtension->MouseTimeoutState) + { + case TimeoutStart: + { + DeviceExtension->MouseTimeoutState = NoChange; + if (DeviceExtension->MouseTimeoutActive && + !KeCancelTimer(&DeviceExtension->TimerMouseTimeout)) + { + /* The timer fired already, give up */ + DeviceExtension->MouseTimeoutActive = FALSE; + return; + } + + Timeout.QuadPart = -15000000; /* 1.5 seconds, should be enough */ + + KeSetTimer( + &DeviceExtension->TimerMouseTimeout, + Timeout, + &DeviceExtension->DpcMouseTimeout); + DeviceExtension->MouseTimeoutActive = TRUE; + return; + } + + case TimeoutCancel: + { + DeviceExtension->MouseTimeoutState = NoChange; + KeCancelTimer(&DeviceExtension->TimerMouseTimeout); + DeviceExtension->MouseTimeoutActive = FALSE; + } + + default: + ;/* nothing, don't want a warning */ + } + + /* Should be unlikely */ + if (!DeviceExtension->MouseComplete) + return; + + Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); + + DeviceExtension->MouseComplete = FALSE; + MouseInBufferCopy = DeviceExtension->MouseInBuffer; + + KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); + + DPRINT("Send a mouse packet\n"); + + if (!DeviceExtension->MouseData.ClassService) + return; + + DPRINT("Sending %lu mouse move(s)\n", MouseInBufferCopy); + (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->MouseData.ClassService)( + DeviceExtension->MouseData.ClassDeviceObject, + DeviceExtension->MouseBuffer, + DeviceExtension->MouseBuffer + MouseInBufferCopy, + &MouseTransferred); + + Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); + DeviceExtension->MouseInBuffer -= MouseTransferred; + if (DeviceExtension->MouseInBuffer) + RtlMoveMemory( + DeviceExtension->MouseBuffer, + DeviceExtension->MouseBuffer + MouseTransferred, + DeviceExtension->MouseInBuffer * sizeof(MOUSE_INPUT_DATA)); + KeReleaseInterruptSpinLock(PortDeviceExtension->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. + */ +static VOID NTAPI +i8042DpcRoutineMouseTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + PI8042_MOUSE_EXTENSION DeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + KIRQL Irql; + + DeviceExtension = (PI8042_MOUSE_EXTENSION)DeferredContext; + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + + Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); + + DPRINT1("Mouse initialization timeout! (substate %x). Disabling mouse.\n", + DeviceExtension->MouseResetState); + + i8042Flush(PortDeviceExtension); + i8042ChangeMode(PortDeviceExtension, CCB_MOUSE_INT_ENAB, CCB_MOUSE_DISAB); + i8042Flush(PortDeviceExtension); + + PortDeviceExtension->Flags &= ~MOUSE_PRESENT; + + KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); +} + +/* + * Runs the mouse IOCTL_INTERNAL dispatch. + */ +NTSTATUS NTAPI +i8042MouInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + PI8042_MOUSE_EXTENSION DeviceExtension; + NTSTATUS Status; + + Stack = IoGetCurrentIrpStackLocation(Irp); + Irp->IoStatus.Information = 0; + DeviceExtension = (PI8042_MOUSE_EXTENSION)DeviceObject->DeviceExtension; + + switch (Stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_INTERNAL_MOUSE_CONNECT: + { + SIZE_T Size; + PIO_WORKITEM WorkItem = NULL; + PI8042_HOOK_WORKITEM WorkItemData = NULL; + + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_CONNECT\n"); + if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA)) + { + Status = STATUS_INVALID_PARAMETER; + goto cleanup; + } + + DeviceExtension->MouseData = + *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer); + + /* Send IOCTL_INTERNAL_I8042_HOOK_MOUSE to device stack */ + WorkItem = IoAllocateWorkItem(DeviceObject); + if (!WorkItem) + { + DPRINT("IoAllocateWorkItem() failed\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + WorkItemData = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(I8042_HOOK_WORKITEM), + I8042PRT_TAG); + if (!WorkItemData) + { + DPRINT("ExAllocatePoolWithTag() failed\n"); + Status = STATUS_NO_MEMORY; + goto cleanup; + } + WorkItemData->WorkItem = WorkItem; + WorkItemData->Irp = Irp; + + /* Initialize extension */ + DeviceExtension->Common.Type = Mouse; + Size = DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize * sizeof(MOUSE_INPUT_DATA); + DeviceExtension->MouseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + Size, + I8042PRT_TAG); + if (!DeviceExtension->MouseBuffer) + { + DPRINT("ExAllocatePoolWithTag() failed\n"); + Status = STATUS_NO_MEMORY; + goto cleanup; + } + RtlZeroMemory(DeviceExtension->MouseBuffer, Size); + DeviceExtension->MouseAttributes.InputDataQueueLength = + DeviceExtension->Common.PortDeviceExtension->Settings.MouseDataQueueSize; + KeInitializeDpc( + &DeviceExtension->DpcMouse, + i8042MouDpcRoutine, + DeviceExtension); + KeInitializeDpc( + &DeviceExtension->DpcMouseTimeout, + i8042DpcRoutineMouseTimeout, + DeviceExtension); + KeInitializeTimer(&DeviceExtension->TimerMouseTimeout); + DeviceExtension->Common.PortDeviceExtension->MouseExtension = DeviceExtension; + DeviceExtension->Common.PortDeviceExtension->Flags |= MOUSE_CONNECTED; + + IoMarkIrpPending(Irp); + DeviceExtension->MouseHook.IsrWritePort = i8042MouIsrWritePort; + DeviceExtension->MouseHook.QueueMousePacket = i8042MouQueuePacket; + DeviceExtension->MouseHook.CallContext = DeviceExtension; + IoQueueWorkItem(WorkItem, + i8042SendHookWorkItem, + DelayedWorkQueue, + WorkItemData); + Status = STATUS_PENDING; + break; + +cleanup: + if (DeviceExtension->MouseBuffer) + ExFreePoolWithTag(DeviceExtension->MouseBuffer, I8042PRT_TAG); + if (WorkItem) + IoFreeWorkItem(WorkItem); + if (WorkItemData) + ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); + break; + } + case IOCTL_INTERNAL_MOUSE_DISCONNECT: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_MOUSE_DISCONNECT\n"); + /* MSDN says that operation is to implemented. + * To implement it, we just have to do: + * DeviceExtension->MouseData.ClassService = NULL; + */ + Status = STATUS_NOT_IMPLEMENTED; + break; + } + case IOCTL_INTERNAL_I8042_HOOK_MOUSE: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_MOUSE\n"); + /* Nothing to do here */ + Status = STATUS_SUCCESS; + break; + } + default: + { + DPRINT("IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n", + Stack->Parameters.DeviceIoControl.IoControlCode); + ASSERT(FALSE); + return ForwardIrpAndForget(DeviceObject, Irp); + } + } + + Irp->IoStatus.Status = Status; + if (Status != STATUS_PENDING) + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; } -#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. @@ -50,56 +490,63 @@ static NTSTATUS STDCALL I8042SynchWritePortMouse(PVOID Context, * generally unlucky). Also note the input parsing routine where we * drop invalid input packets. */ -static VOID STDCALL I8042MouseInputTestTimeout(PDEVICE_EXTENSION DevExt) +static VOID +i8042MouInputTestTimeout( + IN PI8042_MOUSE_EXTENSION DeviceExtension) { ULARGE_INTEGER Now; - if (DevExt->MouseState == MouseExpectingACK || - DevExt->MouseState == MouseResetting) + if (DeviceExtension->MouseState == MouseExpectingACK || + DeviceExtension->MouseState == MouseResetting) return; Now.QuadPart = KeQueryInterruptTime(); - if (DevExt->MouseState != MouseIdle) { + if (DeviceExtension->MouseState != MouseIdle) { /* Check if the last byte came too long ago */ - if (Now.QuadPart - DevExt->MousePacketStartTime.QuadPart > - DevExt->Settings.MouseSynchIn100ns) { + if (Now.QuadPart - DeviceExtension->MousePacketStartTime.QuadPart > + DeviceExtension->Common.PortDeviceExtension->Settings.MouseSynchIn100ns) + { DPRINT("Mouse input packet timeout\n"); - DevExt->MouseState = MouseIdle; + DeviceExtension->MouseState = MouseIdle; } } - if (DevExt->MouseState == MouseIdle) - DevExt->MousePacketStartTime.QuadPart = Now.QuadPart; + if (DeviceExtension->MouseState == MouseIdle) + DeviceExtension->MousePacketStartTime.QuadPart = Now.QuadPart; } /* - * Call the customization hook. The Ret2 parameter is about wether + * Call the customization hook. The ToReturn 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) +static BOOLEAN +i8042MouCallIsrHook( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN UCHAR Status, + IN UCHAR Input, + OUT 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 (DeviceExtension->MouseHook.IsrRoutine) + { + HookReturn = DeviceExtension->MouseHook.IsrRoutine( + DeviceExtension->MouseHook.Context, + DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer, + &DeviceExtension->Common.PortDeviceExtension->Packet, + Status, + &Input, + &HookContinue, + &DeviceExtension->MouseState, + &DeviceExtension->MouseResetState); - if (!HookContinue) { + if (!HookContinue) + { *ToReturn = HookReturn; return TRUE; } @@ -107,852 +554,501 @@ static BOOLEAN STDCALL I8042MouseCallIsrHook(PDEVICE_EXTENSION DevExt, return FALSE; } -static BOOLEAN STDCALL I8042MouseResetIsr(PDEVICE_EXTENSION DevExt, - UCHAR Status, - PUCHAR Value) +static BOOLEAN +i8042MouResetIsr( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN UCHAR Status, + IN UCHAR Value) { + PPORT_DEVICE_EXTENSION PortDeviceExtension; BOOLEAN ToReturn = FALSE; - if (I8042MouseCallIsrHook(DevExt, Status, Value, &ToReturn)) + if (i8042MouCallIsrHook(DeviceExtension, Status, Value, &ToReturn)) return ToReturn; - if (MouseResetting != DevExt->MouseState) { + if (MouseResetting != DeviceExtension->MouseState) return FALSE; - } - DevExt->MouseTimeoutState = TimeoutStart; + DeviceExtension->MouseTimeoutState = TimeoutStart; + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; - 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; + switch (DeviceExtension->MouseResetState) + { + case 1100: /* the first ack, drop it. */ + DeviceExtension->MouseResetState = ExpectingReset; + return TRUE; + case ExpectingReset: + /* First, 0xFF is sent. The mouse is supposed to say AA00 if ok, FC00 if not. */ + if (0xAA == Value) + { + DeviceExtension->MouseResetState++; + } + else + { + PortDeviceExtension->Flags &= ~MOUSE_PRESENT; + DeviceExtension->MouseState = MouseIdle; + DPRINT("Mouse returned bad reset reply: %x (expected aa)\n", Value); + } + return TRUE; + case ExpectingResetId: + if (0x00 == Value) + { + DeviceExtension->MouseResetState++; + DeviceExtension->MouseType = GenericPS2; + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2); + } + else + { + PortDeviceExtension->Flags &= ~MOUSE_PRESENT; + DeviceExtension->MouseState = MouseIdle; + DPRINT1("Mouse returned bad reset reply part two: %x (expected 0)\n", Value); + } + return TRUE; + case ExpectingGetDeviceIdACK: + if (MOUSE_ACK == Value) + { + DeviceExtension->MouseResetState++; + } + else if (MOUSE_NACK == Value || MOUSE_ERROR == Value) + { + DeviceExtension->MouseResetState++; + /* Act as if 00 (normal mouse) was received */ + DPRINT("Mouse doesn't support 0xd2, (returns %x, expected %x), faking\n", Value, MOUSE_ACK); + i8042MouResetIsr(DeviceExtension, Status, 0); + } + return TRUE; + case ExpectingGetDeviceIdValue: + switch (Value) + { + case 0x02: + DeviceExtension->MouseAttributes.MouseIdentifier = + BALLPOINT_I8042_HARDWARE; + break; + case 0x03: + case 0x04: + DeviceExtension->MouseAttributes.MouseIdentifier = + WHEELMOUSE_I8042_HARDWARE; + break; + default: + DeviceExtension->MouseAttributes.MouseIdentifier = + MOUSE_I8042_HARDWARE; + } + DeviceExtension->MouseResetState++; + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8); + return TRUE; + case ExpectingSetResolutionDefaultACK: + DeviceExtension->MouseResetState++; + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x00); + return TRUE; + case ExpectingSetResolutionDefaultValueACK: + DeviceExtension->MouseResetState = ExpectingSetScaling1to1ACK; + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6); + return TRUE; + case ExpectingSetScaling1to1ACK: + case ExpectingSetScaling1to1ACK2: + DeviceExtension->MouseResetState++; + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE6); + return TRUE; + case ExpectingSetScaling1to1ACK3: + DeviceExtension->MouseResetState++; + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE9); + return TRUE; + case ExpectingReadMouseStatusACK: + DeviceExtension->MouseResetState++; + return TRUE; + case ExpectingReadMouseStatusByte1: + DeviceExtension->MouseLogiBuffer[0] = Value; + DeviceExtension->MouseResetState++; + return TRUE; + case ExpectingReadMouseStatusByte2: + DeviceExtension->MouseLogiBuffer[1] = Value; + DeviceExtension->MouseResetState++; + return TRUE; + case ExpectingReadMouseStatusByte3: + DeviceExtension->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 (DeviceExtension->MouseLogiBuffer[1]) + { + DeviceExtension->MouseAttributes.NumberOfButtons = + DeviceExtension->MouseLogiBuffer[1]; + DeviceExtension->MouseType = Ps2pp; + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); + DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK; + /* TODO: Go through EnableWheel and Enable5Buttons */ + return TRUE; + } + DeviceExtension->MouseResetState = EnableWheel; + i8042MouResetIsr(DeviceExtension, Status, Value); + return TRUE; + case EnableWheel: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); + DeviceExtension->MouseResetState = 1001; + return TRUE; + case 1001: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8); + DeviceExtension->MouseResetState++; + return TRUE; + case 1002: + case 1004: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); + DeviceExtension->MouseResetState++; + return TRUE; + case 1003: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x64); + DeviceExtension->MouseResetState++; + return TRUE; + case 1005: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50); + DeviceExtension->MouseResetState++; + return TRUE; + case 1006: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2); + DeviceExtension->MouseResetState++; + return TRUE; + case 1007: + /* Ignore ACK */ + DeviceExtension->MouseResetState++; + return TRUE; + case 1008: + if (0x03 == Value) { + /* It's either an Intellimouse or Intellimouse Explorer. */ + DeviceExtension->MouseAttributes.NumberOfButtons = 3; + DeviceExtension->MouseAttributes.MouseIdentifier = + WHEELMOUSE_I8042_HARDWARE; + DeviceExtension->MouseType = Intellimouse; + DeviceExtension->MouseResetState = Enable5Buttons; + i8042MouResetIsr(DeviceExtension, Status, Value); + } + else + { + /* Just set the default settings and be done */ + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); + DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK; + } + return TRUE; + case Enable5Buttons: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); + DeviceExtension->MouseResetState = 1021; + return TRUE; + case 1022: + case 1024: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); + DeviceExtension->MouseResetState++; + return TRUE; + case 1021: + case 1023: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xC8); + DeviceExtension->MouseResetState++; + return TRUE; + case 1025: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x50); + DeviceExtension->MouseResetState++; + return TRUE; + case 1026: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF2); + DeviceExtension->MouseResetState++; + return TRUE; + case 1027: + if (0x04 == Value) + { + DeviceExtension->MouseAttributes.NumberOfButtons = 5; + DeviceExtension->MouseAttributes.MouseIdentifier = + WHEELMOUSE_I8042_HARDWARE; + DeviceExtension->MouseType = IntellimouseExplorer; + } + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF3); + DeviceExtension->MouseResetState = ExpectingSetSamplingRateACK; + return TRUE; + case ExpectingSetSamplingRateACK: + DeviceExtension->MouseHook.IsrWritePort( + DeviceExtension->MouseHook.CallContext, + (UCHAR)DeviceExtension->MouseAttributes.SampleRate); + DeviceExtension->MouseResetState++; + return TRUE; + case ExpectingSetSamplingRateValueACK: + if (MOUSE_NACK == Value) + { + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0x3C); + DeviceExtension->MouseAttributes.SampleRate = (USHORT)PortDeviceExtension->Settings.SampleRate; + DeviceExtension->MouseResetState = 1040; + return TRUE; + } + case 1040: /* Fallthrough */ + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xE8); + DeviceExtension->MouseResetState = ExpectingFinalResolutionACK; + return TRUE; + case ExpectingFinalResolutionACK: + DeviceExtension->MouseHook.IsrWritePort( + DeviceExtension->MouseHook.CallContext, + (UCHAR)(PortDeviceExtension->Settings.MouseResolution & 0xff)); + DPRINT("Mouse resolution %lu\n", + PortDeviceExtension->Settings.MouseResolution); + DeviceExtension->MouseResetState = ExpectingFinalResolutionValueACK; + return TRUE; + case ExpectingFinalResolutionValueACK: + DeviceExtension->MouseHook.IsrWritePort(DeviceExtension->MouseHook.CallContext, 0xF4); + DeviceExtension->MouseResetState = ExpectingEnableACK; + return TRUE; + case ExpectingEnableACK: + DeviceExtension->MouseState = MouseIdle; + DeviceExtension->MouseTimeoutState = TimeoutCancel; + DPRINT("Mouse type = %u\n", DeviceExtension->MouseType); + return TRUE; 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; + if (DeviceExtension->MouseResetState < 100 || DeviceExtension->MouseResetState > 999) + DPRINT1("MouseResetState went out of range: %lu\n", DeviceExtension->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) +BOOLEAN NTAPI +i8042MouInterruptService( + IN PKINTERRUPT Interrupt, + PVOID Context) { + PI8042_MOUSE_EXTENSION DeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + ULONG Counter; 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) + DeviceExtension = (PI8042_MOUSE_EXTENSION)Context; + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + Counter = PortDeviceExtension->Settings.PollStatusIterations; + + while (Counter) + { + Status = i8042ReadStatus(PortDeviceExtension, &PortStatus); + if (!NT_SUCCESS(Status)) + { + DPRINT("i8042ReadStatus() failed with status 0x%08lx\n", Status); + return FALSE; + } + Status = i8042ReadMouseData(PortDeviceExtension, &Output); + if (NT_SUCCESS(Status)) break; KeStallExecutionProcessor(1); - } while (Iterations < DevExt->Settings.PollStatusIterations); - - if (STATUS_SUCCESS != Status) { - DPRINT1("Spurious I8042 mouse interrupt\n"); + Counter--; + } + if (Counter == 0) + { + DPRINT("Spurious i8042 mouse interrupt\n"); return FALSE; } - DPRINT("Got: %x\n", Output); + DPRINT("Got: 0x%02x\n", Output); - if (I8042PacketIsr(DevExt, Output)) { - if (DevExt->PacketComplete) { + if (i8042PacketIsr(PortDeviceExtension, Output)) + { + if (PortDeviceExtension->PacketComplete) + { DPRINT("Packet complete\n"); - KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL); + KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL); } DPRINT("Irq eaten by packet\n"); return TRUE; } - I8042MouseInputTestTimeout(DevExt); + DPRINT("Irq is mouse input\n"); - if (I8042MouseResetIsr(DevExt, PortStatus, &Output)) { + i8042MouInputTestTimeout(DeviceExtension); + + if (i8042MouResetIsr(DeviceExtension, PortStatus, Output)) + { DPRINT("Handled by ResetIsr or hooked Isr\n"); - if (NoChange != DevExt->MouseTimeoutState) { - KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL); + if (NoChange != DeviceExtension->MouseTimeoutState) { + KeInsertQueueDpc(&DeviceExtension->DpcMouse, NULL, NULL); } return TRUE; } - if (DevExt->MouseType == Ps2pp) - I8042MouseHandlePs2pp(DevExt, Output); + if (DeviceExtension->MouseType == Ps2pp) + i8042MouHandlePs2pp(DeviceExtension, Output); else - I8042MouseHandle(DevExt, Output); + i8042MouHandle(DeviceExtension, Output); return TRUE; } -VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc, - PVOID DeferredContext, - PVOID SystemArgument1, - PVOID SystemArgument2) +/**************************************************************************************** + * WARNING: the mouse initialization code has been taken from old ReactOS mouse driver, * + * named psaux.sys, which has been deleted in revision 14938. * + * Coding style and structures are not exactly the same as in the other parts of the * + * i8042prt driver, but code is supposed to work. * + ****************************************************************************************/ + +#define CONTROLLER_COMMAND_MOUSE_ENABLE 0xA8 +#define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_GETID (0x0200 | MOU_CMD_GET_ID) +#define PSMOUSE_CMD_RESET_BAT (0x0200 | MOU_CMD_RESET) +#define PSMOUSE_RET_ACK MOUSE_ACK +#define PSMOUSE_RET_NAK MOUSE_NACK + +typedef struct _I8042_MOUSE_EXTENSION_OLD { - PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1; - ULONG MouseTransferred = 0; - ULONG MouseInBufferCopy; - KIRQL Irql; - LARGE_INTEGER Timeout; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + UCHAR pkt[8]; + UCHAR ack; + ULONG RepliesExpected; +} I8042_MOUSE_EXTENSION_OLD, *PI8042_MOUSE_EXTENSION_OLD; - 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) +/* Sends a byte to the mouse */ +static INT SendByte( + IN PI8042_MOUSE_EXTENSION_OLD DeviceExtension, + IN UCHAR byte) { - PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)DeferredContext; - KIRQL Irql; + INT timeout = 100; /* 100 msec */ + UCHAR scancode; + LARGE_INTEGER Millisecond_Timeout; - Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt); + Millisecond_Timeout.QuadPart = -10000L; - 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; + DeviceExtension->ack = 0; + i8042IsrWritePort(DeviceExtension->PortDeviceExtension, byte, CTRL_WRITE_MOUSE); + while ((DeviceExtension->ack == 0) && timeout--) + { + if (i8042ReadKeyboardData(DeviceExtension->PortDeviceExtension, &scancode)) { - 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; + switch(scancode) + { + case PSMOUSE_RET_ACK: + DeviceExtension->ack = 1; + break; + case PSMOUSE_RET_NAK: + DeviceExtension->ack = -1; + break; + default: + DeviceExtension->ack = 1; /* Workaround for mice which don't ACK the Get ID command */ + if (DeviceExtension->RepliesExpected) + DeviceExtension->pkt[--DeviceExtension->RepliesExpected] = scancode; + break; } - - 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; + return (INT)(-(DeviceExtension->ack <= 0)); } - - 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; + KeDelayExecutionThread(KernelMode, FALSE, &Millisecond_Timeout); } - -intcontfailure: - return Irp->IoStatus.Status; + return (INT)(-(DeviceExtension->ack <= 0)); } -BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt) +/* Send a PS/2 command to the mouse. */ +static INT SendCommand( + IN PI8042_MOUSE_EXTENSION_OLD DeviceExtension, + IN PUCHAR param, + IN INT command) { - UCHAR Value; - NTSTATUS Status; + LARGE_INTEGER Millisecond_Timeout; + UCHAR scancode; + INT timeout = 500; /* 500 msec */ + UCHAR send = (command >> 12) & 0xf; + UCHAR receive = (command >> 8) & 0xf; + UCHAR i; - DPRINT("Enabling mouse\n"); + Millisecond_Timeout.QuadPart = -10000L; - if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { - DPRINT1("Can't read i8042 mode\n"); - return FALSE; + DeviceExtension->RepliesExpected = receive; + if (command == PSMOUSE_CMD_RESET_BAT) + timeout = 2000; /* 2 sec */ + + if (command & 0xff) + if (SendByte(DeviceExtension, command & 0xff)) + return (INT)(DeviceExtension->RepliesExpected = 0) - 1; + + for (i = 0; i < send; i++) + if (SendByte(DeviceExtension, param[i])) + return (INT)(DeviceExtension->RepliesExpected = 0) - 1; + + while (DeviceExtension->RepliesExpected && timeout--) + { + if (DeviceExtension->RepliesExpected == 1 && command == PSMOUSE_CMD_RESET_BAT) + timeout = 100; + + if (DeviceExtension->RepliesExpected == 1 && command == PSMOUSE_CMD_GETID && + DeviceExtension->pkt[1] != 0xab && DeviceExtension->pkt[1] != 0xac) + { + DeviceExtension->RepliesExpected = 0; + break; + } + + if (i8042ReadKeyboardData(DeviceExtension->PortDeviceExtension, &scancode)) + { + DeviceExtension->pkt[--DeviceExtension->RepliesExpected] = scancode; + } + + KeDelayExecutionThread (KernelMode, FALSE, &Millisecond_Timeout); } - Status = I8042ReadDataWait(DevExt, &Value); - if (!NT_SUCCESS(Status)) { - DPRINT1("No response after read i8042 mode\n"); - return FALSE; - } + for (i = 0; i < receive; i++) + param[i] = DeviceExtension->pkt[(receive - 1) - i]; - Value &= ~(0x20); // don't disable mouse - Value |= 0x02; // enable mouse interrupts + if (DeviceExtension->RepliesExpected) + return (int)(DeviceExtension->RepliesExpected = 0) - 1; - 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; + return 0; } -BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt) +/* Detect if mouse is just a standard ps/2 mouse */ +static BOOLEAN TestMouse( + IN PI8042_MOUSE_EXTENSION_OLD DeviceExtension) { - UCHAR Value; - NTSTATUS Status; + UCHAR param[4]; - DPRINT("Disabling mouse\n"); + param[0] = param[1] = 0xa5; - I8042Flush(); /* Just to be (kind of) sure */ + /* + * First, we check if it's a mouse. It should send 0x00 or 0x03 + * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. + */ + if(SendCommand(DeviceExtension, param, PSMOUSE_CMD_GETID)) + return -1; - if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) { - DPRINT1("Can't read i8042 mode\n"); - return FALSE; - } + if(param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04) + return -1; - 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. + /* + * Then we reset and disable the mouse so that it doesn't generate events. */ return TRUE; } -BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt) +/* Initialize the PS/2 mouse support */ +static BOOLEAN SetupMouse( + IN PI8042_MOUSE_EXTENSION_OLD DeviceExtension) { - BOOLEAN Ok = TRUE; - NTSTATUS Status; - UCHAR Value; - UCHAR ExpectedReply[] = { 0xFA, 0xAA, 0x00 }; - unsigned ReplyByte; - ULONG Counter; + LARGE_INTEGER Millisecond_Timeout; - I8042Flush(); + Millisecond_Timeout.QuadPart = -10000L; - if (! I8042Write(DevExt, I8042_CTRL_PORT, 0xD4) || - ! I8042Write(DevExt, I8042_DATA_PORT, 0xFF)) + /* setup */ + DeviceExtension->RepliesExpected = 0; + DeviceExtension->ack = 0; + + /* Enable the PS/2 mouse port */ + i8042Write(DeviceExtension->PortDeviceExtension, DeviceExtension->PortDeviceExtension->ControlPort, CONTROLLER_COMMAND_MOUSE_ENABLE); + + if (TestMouse(DeviceExtension)) { - DPRINT1("Failed to write reset command to mouse\n"); - Ok = FALSE; + DPRINT("Detected Mouse\n"); + + if (SendCommand(DeviceExtension, NULL, PSMOUSE_CMD_ENABLE)) + DPRINT1("Failed to enable mouse!\n"); } - for (ReplyByte = 0; - ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]) && Ok; - ReplyByte++) - { - Counter = 500; - - do { - Status = I8042ReadDataWait(DevExt, &Value); - } while (Status == STATUS_IO_TIMEOUT && Counter--); - - 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; + return TRUE; +} + +static NTSTATUS OldInitialization( + IN PPORT_DEVICE_EXTENSION PortDeviceExtension) +{ + I8042_MOUSE_EXTENSION_OLD DeviceExtension; + + RtlZeroMemory(&DeviceExtension, sizeof(I8042_MOUSE_EXTENSION_OLD)); + DeviceExtension.PortDeviceExtension = PortDeviceExtension; + SetupMouse(&DeviceExtension); + return STATUS_SUCCESS; } diff --git a/reactos/drivers/input/i8042prt/pnp.c b/reactos/drivers/input/i8042prt/pnp.c new file mode 100644 index 00000000000..88f17bb8c64 --- /dev/null +++ b/reactos/drivers/input/i8042prt/pnp.c @@ -0,0 +1,684 @@ +/* + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/pnp.c + * PURPOSE: IRP_MJ_PNP operations + * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +/* INCLUDES ******************************************************************/ + +#include "i8042prt.h" + +/* FUNCTIONS *****************************************************************/ + +/* 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 +i8042ChangeMode( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR FlagsToDisable, + IN UCHAR FlagsToEnable) +{ + UCHAR Value; + NTSTATUS Status; + + if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, KBD_READ_MODE)) + { + DPRINT1("Can't read i8042 mode\n"); + return FALSE; + } + + Status = i8042ReadDataWait(DeviceExtension, &Value); + if (!NT_SUCCESS(Status)) + { + DPRINT1("No response after read i8042 mode\n"); + return FALSE; + } + + Value &= ~FlagsToDisable; + Value |= FlagsToEnable; + + if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, KBD_WRITE_MODE)) + { + DPRINT1("Can't set i8042 mode\n"); + return FALSE; + } + + if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Value)) + { + DPRINT1("Can't send i8042 mode\n"); + return FALSE; + } + + return TRUE; +} + +static NTSTATUS +i8042BasicDetect( + IN PPORT_DEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS Status; + UCHAR Value = 0; + + /* Don't enable keyboard and mouse interrupts, disable keyboard/mouse */ + if (!i8042ChangeMode(DeviceExtension, CCB_KBD_INT_ENAB | CCB_MOUSE_INT_ENAB, CCB_KBD_DISAB | CCB_MOUSE_DISAB)) + return STATUS_IO_DEVICE_ERROR; + + i8042Flush(DeviceExtension); + + if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_SELF_TEST)) + { + DPRINT1("Writing CTRL_SELF_TEST command failed\n"); + return STATUS_IO_TIMEOUT; + } + + Status = i8042ReadDataWait(DeviceExtension, &Value); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to read CTRL_SELF_TEST response, status 0x%08lx\n", Status); + return Status; + } + + if (Value != 0x55) + { + DPRINT1("Got 0x%02x instead of 0x55\n", Value); + return STATUS_IO_DEVICE_ERROR; + } + + /* + * We used to send a KBD_LINE_TEST (0xAB) 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. + */ + DeviceExtension->Flags |= KEYBOARD_PRESENT; + + if (i8042Write(DeviceExtension, DeviceExtension->ControlPort, MOUSE_LINE_TEST)) + { + Status = i8042ReadDataWait(DeviceExtension, &Value); + if (NT_SUCCESS(Status) && Value == 0) + DeviceExtension->Flags |= MOUSE_PRESENT; + } + + if (IsFirstStageSetup()) + /* Ignore the mouse */ + DeviceExtension->Flags &= ~MOUSE_PRESENT; + + return STATUS_SUCCESS; +} + +static BOOLEAN +i8042DetectKeyboard( + IN PPORT_DEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS Status; + + /* Set LEDs (that is not fatal if some error occurs) */ + Status = i8042SynchWritePort(DeviceExtension, 0, KBD_CMD_SET_LEDS, TRUE); + if (NT_SUCCESS(Status)) + { + Status = i8042SynchWritePort(DeviceExtension, 0, 0, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT("Can't finish SET_LEDS (0x%08lx)\n", Status); + return FALSE; + } + } + else + { + DPRINT("Warning: can't write SET_LEDS (0x%08lx)\n", Status); + } + + /* Turn on translation and SF (Some machines don't reboot if SF is not set) */ + if (!i8042ChangeMode(DeviceExtension, 0, CCB_TRANSLATE | CCB_SYSTEM_FLAG)) + return FALSE; + + return TRUE; +} + +static BOOLEAN +i8042DetectMouse( + IN PPORT_DEVICE_EXTENSION DeviceExtension) +{ + BOOLEAN Ok = FALSE; + NTSTATUS Status; + UCHAR Value; + UCHAR ExpectedReply[] = { MOUSE_ACK, 0xAA, 0x00 }; + UCHAR ReplyByte; + + i8042Flush(DeviceExtension); + + if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_WRITE_MOUSE) + ||!i8042Write(DeviceExtension, DeviceExtension->DataPort, MOU_CMD_RESET)) + { + DPRINT1("Failed to write reset command to mouse\n"); + goto cleanup; + } + + for (ReplyByte = 0; + ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]); + ReplyByte++) + { + ULONG Counter = 500; + + do + { + Status = i8042ReadDataWait(DeviceExtension, &Value); + } while (Status == STATUS_IO_TIMEOUT && Counter--); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("No ACK after mouse reset, status 0x%08lx\n", Status); + goto cleanup; + } + else if (Value != ExpectedReply[ReplyByte]) + { + DPRINT1("Unexpected reply: 0x%02x (expected 0x%02x)\n", + Value, ExpectedReply[ReplyByte]); + goto cleanup; + } + } + + Ok = TRUE; + +cleanup: + 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(DeviceExtension); + i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_SELF_TEST); + i8042ReadDataWait(DeviceExtension, &Value); + i8042Flush(DeviceExtension); + } + + DPRINT("Mouse %sdetected\n", Ok ? "" : "not "); + + return Ok; +} + +static NTSTATUS +i8042ConnectKeyboardInterrupt( + IN PI8042_KEYBOARD_EXTENSION DeviceExtension) +{ + PPORT_DEVICE_EXTENSION PortDeviceExtension; + KIRQL DirqlMax; + NTSTATUS Status; + + DPRINT("i8042ConnectKeyboardInterrupt()\n"); + + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + DirqlMax = MAX( + PortDeviceExtension->KeyboardInterrupt.Dirql, + PortDeviceExtension->MouseInterrupt.Dirql); + + DPRINT("KeyboardInterrupt.Vector %lu\n", + PortDeviceExtension->KeyboardInterrupt.Vector); + DPRINT("KeyboardInterrupt.Dirql %lu\n", + PortDeviceExtension->KeyboardInterrupt.Dirql); + DPRINT("KeyboardInterrupt.DirqlMax %lu\n", + DirqlMax); + DPRINT("KeyboardInterrupt.InterruptMode %s\n", + PortDeviceExtension->KeyboardInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched"); + DPRINT("KeyboardInterrupt.ShareInterrupt %s\n", + PortDeviceExtension->KeyboardInterrupt.ShareInterrupt ? "yes" : "no"); + DPRINT("KeyboardInterrupt.Affinity 0x%lx\n", + PortDeviceExtension->KeyboardInterrupt.Affinity); + Status = IoConnectInterrupt( + &PortDeviceExtension->KeyboardInterrupt.Object, + i8042KbdInterruptService, + DeviceExtension, &PortDeviceExtension->SpinLock, + PortDeviceExtension->KeyboardInterrupt.Vector, PortDeviceExtension->KeyboardInterrupt.Dirql, DirqlMax, + PortDeviceExtension->KeyboardInterrupt.InterruptMode, PortDeviceExtension->KeyboardInterrupt.ShareInterrupt, + PortDeviceExtension->KeyboardInterrupt.Affinity, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT("IoConnectInterrupt() failed with status 0x%08x\n", Status); + return Status; + } + + if (DirqlMax == PortDeviceExtension->KeyboardInterrupt.Dirql) + PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object; + PortDeviceExtension->Flags |= KEYBOARD_INITIALIZED; + return STATUS_SUCCESS; +} + +static NTSTATUS +i8042ConnectMouseInterrupt( + IN PI8042_MOUSE_EXTENSION DeviceExtension) +{ + PPORT_DEVICE_EXTENSION PortDeviceExtension; + KIRQL DirqlMax; + NTSTATUS Status; + + DPRINT("i8042ConnectMouseInterrupt()\n"); + + Status = i8042MouInitialize(DeviceExtension); + if (!NT_SUCCESS(Status)) + return Status; + + PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; + DirqlMax = MAX( + PortDeviceExtension->KeyboardInterrupt.Dirql, + PortDeviceExtension->MouseInterrupt.Dirql); + + DPRINT("MouseInterrupt.Vector %lu\n", + PortDeviceExtension->MouseInterrupt.Vector); + DPRINT("MouseInterrupt.Dirql %lu\n", + PortDeviceExtension->MouseInterrupt.Dirql); + DPRINT("MouseInterrupt.DirqlMax %lu\n", + DirqlMax); + DPRINT("MouseInterrupt.InterruptMode %s\n", + PortDeviceExtension->MouseInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched"); + DPRINT("MouseInterrupt.ShareInterrupt %s\n", + PortDeviceExtension->MouseInterrupt.ShareInterrupt ? "yes" : "no"); + DPRINT("MouseInterrupt.Affinity 0x%lx\n", + PortDeviceExtension->MouseInterrupt.Affinity); + Status = IoConnectInterrupt( + &PortDeviceExtension->MouseInterrupt.Object, + i8042MouInterruptService, + DeviceExtension, &PortDeviceExtension->SpinLock, + PortDeviceExtension->MouseInterrupt.Vector, PortDeviceExtension->MouseInterrupt.Dirql, DirqlMax, + PortDeviceExtension->MouseInterrupt.InterruptMode, PortDeviceExtension->MouseInterrupt.ShareInterrupt, + PortDeviceExtension->MouseInterrupt.Affinity, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT("IoConnectInterrupt() failed with status 0x%08x\n", Status); + goto cleanup; + } + + if (DirqlMax == PortDeviceExtension->MouseInterrupt.Dirql) + PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->MouseInterrupt.Object; + + PortDeviceExtension->Flags |= MOUSE_INITIALIZED; + Status = STATUS_SUCCESS; + +cleanup: + if (!NT_SUCCESS(Status)) + { + PortDeviceExtension->Flags &= ~MOUSE_INITIALIZED; + if (PortDeviceExtension->MouseInterrupt.Object) + { + IoDisconnectInterrupt(PortDeviceExtension->MouseInterrupt.Object); + PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object; + } + } + return Status; +} + +static NTSTATUS +EnableInterrupts( + IN PPORT_DEVICE_EXTENSION DeviceExtension) +{ + UCHAR FlagsToDisable = 0; + UCHAR FlagsToEnable = 0; + + i8042Flush(DeviceExtension); + + /* Select the devices we have */ + if (DeviceExtension->Flags & KEYBOARD_PRESENT) + { + FlagsToDisable |= CCB_KBD_DISAB; + FlagsToEnable |= CCB_KBD_INT_ENAB; + } + if (DeviceExtension->Flags & MOUSE_PRESENT) + { + FlagsToDisable |= CCB_MOUSE_DISAB; + FlagsToEnable |= CCB_MOUSE_INT_ENAB; + } + if (!i8042ChangeMode(DeviceExtension, FlagsToDisable, FlagsToEnable)) + return STATUS_UNSUCCESSFUL; + + return STATUS_SUCCESS; +} + +static NTSTATUS +StartProcedure( + IN PPORT_DEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS Status; + + if (DeviceExtension->DataPort == 0) + { + /* Unable to do something at the moment */ + return STATUS_SUCCESS; + } + + if (!(DeviceExtension->Flags & (KEYBOARD_PRESENT | MOUSE_PRESENT))) + { + /* Try to detect them */ + DPRINT("Check if the controller is really a i8042\n"); + Status = i8042BasicDetect(DeviceExtension); + if (!NT_SUCCESS(Status)) + { + DPRINT("i8042BasicDetect() failed with status 0x%08lx\n", Status); + return STATUS_UNSUCCESSFUL; + } + DPRINT("Detecting keyboard\n"); + if (!i8042DetectKeyboard(DeviceExtension)) + return STATUS_UNSUCCESSFUL; + DPRINT("Detecting mouse\n"); + if (!i8042DetectMouse(DeviceExtension)) + return STATUS_UNSUCCESSFUL; + DPRINT("Keyboard present: %s\n", DeviceExtension->Flags & KEYBOARD_PRESENT ? "YES" : "NO"); + DPRINT("Mouse present : %s\n", DeviceExtension->Flags & MOUSE_PRESENT ? "YES" : "NO"); + } + + /* Connect interrupts */ + if (DeviceExtension->Flags & KEYBOARD_PRESENT && + DeviceExtension->Flags & KEYBOARD_CONNECTED && + DeviceExtension->Flags & KEYBOARD_STARTED && + !(DeviceExtension->Flags & (MOUSE_PRESENT | KEYBOARD_INITIALIZED))) + { + /* No mouse, and the keyboard is ready */ + Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension); + if (NT_SUCCESS(Status)) + { + DeviceExtension->Flags |= KEYBOARD_INITIALIZED; + Status = EnableInterrupts(DeviceExtension); + } + } + else if (DeviceExtension->Flags & MOUSE_PRESENT && + DeviceExtension->Flags & MOUSE_CONNECTED && + DeviceExtension->Flags & MOUSE_STARTED && + !(DeviceExtension->Flags & (KEYBOARD_PRESENT | MOUSE_INITIALIZED))) + { + /* No keyboard, and the mouse is ready */ + Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension); + if (NT_SUCCESS(Status)) + { + DeviceExtension->Flags |= MOUSE_INITIALIZED; + Status = EnableInterrupts(DeviceExtension); + } + } + else if (DeviceExtension->Flags & KEYBOARD_PRESENT && + DeviceExtension->Flags & KEYBOARD_CONNECTED && + DeviceExtension->Flags & KEYBOARD_STARTED && + DeviceExtension->Flags & MOUSE_PRESENT && + DeviceExtension->Flags & MOUSE_CONNECTED && + DeviceExtension->Flags & MOUSE_STARTED && + !(DeviceExtension->Flags & (KEYBOARD_INITIALIZED | MOUSE_INITIALIZED))) + { + /* The keyboard and mouse are ready */ + Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension); + if (NT_SUCCESS(Status)) + { + DeviceExtension->Flags |= KEYBOARD_INITIALIZED; + Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension); + if (NT_SUCCESS(Status)) + { + DeviceExtension->Flags |= MOUSE_INITIALIZED; + Status = EnableInterrupts(DeviceExtension); + } + } + } + else + { + /* Nothing to do */ + Status = STATUS_SUCCESS; + } + + return Status; +} + +static NTSTATUS +i8042PnpStartDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PCM_RESOURCE_LIST AllocatedResources, + IN PCM_RESOURCE_LIST AllocatedResourcesTranslated) +{ + PFDO_DEVICE_EXTENSION DeviceExtension; + PPORT_DEVICE_EXTENSION PortDeviceExtension; + PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor, ResourceDescriptorTranslated; + INTERRUPT_DATA InterruptData; + BOOLEAN FoundDataPort = FALSE; + BOOLEAN FoundControlPort = FALSE; + BOOLEAN FoundIrq = FALSE; + ULONG i; + NTSTATUS Status; + + DPRINT("i8042PnpStartDevice(%p)\n", DeviceObject); + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + PortDeviceExtension = DeviceExtension->PortDeviceExtension; + + ASSERT(DeviceExtension->PnpState == dsStopped); + + if (!AllocatedResources) + { + DPRINT("No allocated resources sent to driver\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + if (AllocatedResources->Count != 1) + { + DPRINT("Wrong number of allocated resources sent to driver\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + if (AllocatedResources->List[0].PartialResourceList.Version != 1 + || AllocatedResources->List[0].PartialResourceList.Revision != 1 + || AllocatedResourcesTranslated->List[0].PartialResourceList.Version != 1 + || AllocatedResourcesTranslated->List[0].PartialResourceList.Revision != 1) + { + DPRINT("Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n", + AllocatedResources->List[0].PartialResourceList.Version, + AllocatedResources->List[0].PartialResourceList.Revision, + AllocatedResourcesTranslated->List[0].PartialResourceList.Version, + AllocatedResourcesTranslated->List[0].PartialResourceList.Revision); + return STATUS_REVISION_MISMATCH; + } + + /* Get Irq and optionally control port and data port */ + for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++) + { + ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i]; + ResourceDescriptorTranslated = &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i]; + switch (ResourceDescriptor->Type) + { + case CmResourceTypePort: + { + if (ResourceDescriptor->u.Port.Length == 1) + { + /* We assume that the first ressource will + * be the control port and the second one + * will be the data port... + */ + if (!FoundDataPort) + { + PortDeviceExtension->DataPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart); + DPRINT("Found data port: 0x%lx\n", PortDeviceExtension->DataPort); + FoundDataPort = TRUE; + } + else if (!FoundControlPort) + { + PortDeviceExtension->ControlPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart); + DPRINT("Found control port: 0x%lx\n", PortDeviceExtension->ControlPort); + FoundControlPort = TRUE; + } + else + { + DPRINT("Too much I/O ranges provided\n", ResourceDescriptor->u.Port.Length); + return STATUS_INVALID_PARAMETER; + } + } + else + DPRINT1("Invalid I/O range length: 0x%lx\n", ResourceDescriptor->u.Port.Length); + break; + } + case CmResourceTypeInterrupt: + { + if (FoundIrq) + return STATUS_INVALID_PARAMETER; + InterruptData.Dirql = (KIRQL)ResourceDescriptorTranslated->u.Interrupt.Level; + InterruptData.Vector = ResourceDescriptorTranslated->u.Interrupt.Vector; + InterruptData.Affinity = ResourceDescriptorTranslated->u.Interrupt.Affinity; + if (ResourceDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED) + InterruptData.InterruptMode = Latched; + else + InterruptData.InterruptMode = LevelSensitive; + InterruptData.ShareInterrupt = (ResourceDescriptorTranslated->ShareDisposition == CmResourceShareShared); + DPRINT("Found irq resource: %lu\n", ResourceDescriptor->u.Interrupt.Vector); + FoundIrq = TRUE; + break; + } + default: + DPRINT("Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type); + } + } + + if (!FoundIrq) + { + DPRINT("Interrupt resource was not found in allocated resources list\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + else if (DeviceExtension->Type == Keyboard && (!FoundDataPort || !FoundControlPort)) + { + DPRINT("Some required resources were not found in allocated resources list\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + else if (DeviceExtension->Type == Mouse && (FoundDataPort || FoundControlPort)) + { + DPRINT("Too much resources were provided in allocated resources list\n"); + return STATUS_INVALID_PARAMETER; + } + + switch (DeviceExtension->Type) + { + case Keyboard: + { + RtlCopyMemory( + &PortDeviceExtension->KeyboardInterrupt, + &InterruptData, + sizeof(INTERRUPT_DATA)); + PortDeviceExtension->Flags |= KEYBOARD_STARTED; + Status = StartProcedure(PortDeviceExtension); + break; + } + case Mouse: + { + RtlCopyMemory( + &PortDeviceExtension->MouseInterrupt, + &InterruptData, + sizeof(INTERRUPT_DATA)); + PortDeviceExtension->Flags |= MOUSE_STARTED; + Status = StartProcedure(PortDeviceExtension); + break; + } + default: + { + DPRINT1("Unknown FDO type %u\n", DeviceExtension->Type); + ASSERT(FALSE); + Status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + if (NT_SUCCESS(Status)) + DeviceExtension->PnpState = dsStarted; + + return Status; +} + +NTSTATUS NTAPI +i8042Pnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + ULONG MinorFunction; + I8042_DEVICE_TYPE DeviceType; + ULONG_PTR Information = 0; + NTSTATUS Status; + + Stack = IoGetCurrentIrpStackLocation(Irp); + MinorFunction = Stack->MinorFunction; + DeviceType = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Type; + + switch (MinorFunction) + { + case IRP_MN_START_DEVICE: /* 0x00 */ + { + DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); + + /* Call lower driver (if any) */ + if (DeviceType != PhysicalDeviceObject) + { + Status = ForwardIrpAndWait(DeviceObject, Irp); + if (NT_SUCCESS(Status)) + Status = i8042PnpStartDevice( + DeviceObject, + Stack->Parameters.StartDevice.AllocatedResources, + Stack->Parameters.StartDevice.AllocatedResourcesTranslated); + } + else + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x07 */ + { + switch (Stack->Parameters.QueryDeviceRelations.Type) + { + case BusRelations: + { + PDEVICE_RELATIONS DeviceRelations; + + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n"); + DeviceRelations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), I8042PRT_TAG); + if (DeviceRelations) + { + DeviceRelations->Count = 0; + Information = (ULONG_PTR)DeviceRelations; + Status = STATUS_SUCCESS; + } + else + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + case RemovalRelations: + { + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n"); + return ForwardIrpAndForget(DeviceObject, Irp); + } + default: + DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n", + Stack->Parameters.QueryDeviceRelations.Type); + ASSERT(FALSE); + return ForwardIrpAndForget(DeviceObject, Irp); + } + break; + } + case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0x0d */ + { + DPRINT("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n"); + /* Nothing to do */ + Status = Irp->IoStatus.Status; + break; + } + default: + { + DPRINT1("IRP_MJ_PNP / unknown minor function 0x%x\n", MinorFunction); + ASSERT(FALSE); + return ForwardIrpAndForget(DeviceObject, Irp); + } + } + + Irp->IoStatus.Information = Information; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} diff --git a/reactos/drivers/input/i8042prt/ps2pp.c b/reactos/drivers/input/i8042prt/ps2pp.c index 44db6ff4ef2..c1a40dbdf39 100644 --- a/reactos/drivers/input/i8042prt/ps2pp.c +++ b/reactos/drivers/input/i8042prt/ps2pp.c @@ -1,136 +1,138 @@ /* - * 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 + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/ps2pp.c + * PURPOSE: ps2pp protocol handling + * PROGRAMMERS: Copyright Martijn Vernooij (o112w8r02@sneakemail.com) + * Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) */ /* INCLUDES ****************************************************************/ #include "i8042prt.h" -#ifndef NDEBUG -#define NDEBUG -#endif -#include +/* FUNCTIONS *****************************************************************/ -VOID I8042MouseHandlePs2pp(PDEVICE_EXTENSION DevExt, UCHAR Input) +VOID +i8042MouHandlePs2pp( + IN PI8042_MOUSE_EXTENSION DeviceExtension, + IN UCHAR Input) { UCHAR PktType; - PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer + - DevExt->MouseInBuffer; + PMOUSE_INPUT_DATA MouseInput; -/* 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 - */ + MouseInput = DeviceExtension->MouseBuffer + DeviceExtension->MouseInBuffer; - switch (DevExt->MouseState) { - case MouseIdle: - case XMovement: - DevExt->MouseLogiBuffer[DevExt->MouseState] = Input; - DevExt->MouseState++; - break; + /* 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 + */ - case YMovement: - DevExt->MouseLogiBuffer[2] = Input; - DevExt->MouseState = MouseIdle; + switch (DeviceExtension->MouseState) + { + case MouseIdle: + case XMovement: + DeviceExtension->MouseLogiBuffer[DeviceExtension->MouseState] = Input; + DeviceExtension->MouseState++; + break; - /* first check if it's a normal packet */ + case YMovement: + DeviceExtension->MouseLogiBuffer[2] = Input; + DeviceExtension->MouseState = MouseIdle; - 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; - } + /* first check if it's a normal packet */ - /* 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; + if (!(DeviceExtension->MouseLogiBuffer[0] & 0xC0)) + { + DeviceExtension->MouseState = MouseIdle; + i8042MouHandle(DeviceExtension, DeviceExtension->MouseLogiBuffer[0]); + i8042MouHandle(DeviceExtension, DeviceExtension->MouseLogiBuffer[1]); + i8042MouHandle(DeviceExtension, DeviceExtension->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; } - I8042MouseHandleButtons(DevExt, MOUSE_BUTTON_4_DOWN | - MOUSE_BUTTON_5_DOWN); - I8042QueueMousePacket( - DevExt->MouseObject); - return; + + /* sanity check */ + if (((DeviceExtension->MouseLogiBuffer[0] & 0x48) != 0x48) || + (((DeviceExtension->MouseLogiBuffer[1] & 0x0C) >> 2) != + (DeviceExtension->MouseLogiBuffer[2] & 0x03))) + { + DPRINT1("Ps2pp packet fails sanity checks\n"); + return; + } + + /* Now get the packet type */ + PktType = ((DeviceExtension->MouseLogiBuffer[0] & 0x30) >> 4) & + ((DeviceExtension->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 (DeviceExtension->MouseLogiBuffer[2] & 0x10) + MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN; + + if (DeviceExtension->MouseLogiBuffer[2] & 0x20) + MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN; + + if (DeviceExtension->MouseLogiBuffer[2] & 0x0F) + { + MouseInput->ButtonFlags |= MOUSE_WHEEL; + if (DeviceExtension->MouseLogiBuffer[2] & 0x08) + MouseInput->ButtonData = (DeviceExtension->MouseLogiBuffer[2] & 0x07) - 8; + else + MouseInput->ButtonData = DeviceExtension->MouseLogiBuffer[2] & 0x07; + } + i8042MouHandleButtons( + DeviceExtension, + MOUSE_BUTTON_4_DOWN | MOUSE_BUTTON_5_DOWN); + DeviceExtension->MouseHook.QueueMousePacket(DeviceExtension->MouseHook.CallContext); + return; + default: + /* These are for things that would probably + * be handled by logitechs own driver. + */ + return; + } + default: - /* These are for things that would probably - * be handled by logitechs own driver. - */ - return; - } - default: - DPRINT1("Unexpected input state for ps2pp!\n"); + DPRINT1("Unexpected input state for ps2pp!\n"); } } diff --git a/reactos/drivers/input/i8042prt/readwrite.c b/reactos/drivers/input/i8042prt/readwrite.c new file mode 100644 index 00000000000..e22517390dd --- /dev/null +++ b/reactos/drivers/input/i8042prt/readwrite.c @@ -0,0 +1,212 @@ +/* + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/readwrite.c + * PURPOSE: Read/write port functions + * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) + Copyright Jason Filby (jasonfilby@yahoo.com) + Copyright Martijn Vernooij (o112w8r02@sneakemail.com) + Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +/* INCLUDES ******************************************************************/ + +#include "i8042prt.h" + +/* FUNCTIONS *****************************************************************/ + +VOID +i8042Flush( + IN PPORT_DEVICE_EXTENSION DeviceExtension) +{ + UCHAR Ignore; + + while (NT_SUCCESS(i8042ReadData(DeviceExtension, KBD_OBF | MOU_OBF, &Ignore))) { + DPRINT("Data flushed\n"); /* drop */ + } +} + +VOID +i8042IsrWritePort( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR Value, + IN UCHAR SelectCmd OPTIONAL) +{ + if (SelectCmd) + if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, SelectCmd)) + return; + + i8042Write(DeviceExtension, DeviceExtension->DataPort, Value); +} + +/* + * FUNCTION: Read data from port 0x60 + */ +NTSTATUS +i8042ReadData( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR StatusFlags, + OUT PUCHAR Data) +{ + UCHAR PortStatus; + NTSTATUS Status; + + Status = i8042ReadStatus(DeviceExtension, &PortStatus); + if (!NT_SUCCESS(Status)) + return Status; + + // If data is available + if (PortStatus & StatusFlags) + { + *Data = READ_PORT_UCHAR(DeviceExtension->DataPort); + DPRINT("Read: 0x%02x (status: 0x%x)\n", Data[0], PortStatus); + + // If the data is valid (not timeout, not parity error) + if ((PortStatus & KBD_PERR) == 0) + return STATUS_SUCCESS; + } + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +i8042ReadStatus( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + OUT PUCHAR Status) +{ + ASSERT(DeviceExtension->ControlPort != NULL); + *Status = READ_PORT_UCHAR(DeviceExtension->ControlPort); + return STATUS_SUCCESS; +} + +/* + * FUNCTION: Read data from data port + */ +NTSTATUS +i8042ReadDataWait( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + OUT PUCHAR Data) +{ + ULONG Counter; + NTSTATUS Status; + + Counter = DeviceExtension->Settings.PollingIterations; + + while (Counter--) + { + Status = i8042ReadKeyboardData(DeviceExtension, Data); + + if (NT_SUCCESS(Status)) + return Status; + + KeStallExecutionProcessor(50); + } + + /* Timed out */ + 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. + */ +NTSTATUS DDKAPI +i8042SynchReadPort( + IN PVOID Context, + OUT PUCHAR Value, + IN BOOLEAN WaitForAck) +{ + PPORT_DEVICE_EXTENSION DeviceExtension; + + DeviceExtension = (PPORT_DEVICE_EXTENSION)Context; + + return i8042ReadDataWait(DeviceExtension, Value); +} + +/* + * These functions are callbacks for filter driver custom + * initialization routines. + */ +NTSTATUS NTAPI +i8042SynchWritePort( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN UCHAR Port, + IN UCHAR Value, + IN BOOLEAN WaitForAck) +{ + NTSTATUS Status; + UCHAR Ack; + ULONG ResendIterations; + + ResendIterations = DeviceExtension->Settings.ResendIterations + 1; + + do + { + if (Port) + if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Port)) + { + DPRINT1("Failed to write Port\n"); + return STATUS_IO_TIMEOUT; + } + + if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Value)) + { + DPRINT1("Failed to write Value\n"); + return STATUS_IO_TIMEOUT; + } + + if (WaitForAck) + { + Status = i8042ReadDataWait(DeviceExtension, &Ack); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to read Ack\n"); + return Status; + } + if (Ack == KBD_ACK) + return STATUS_SUCCESS; + else if (Ack == KBD_RESEND) + DPRINT("i8042 asks for a data resend\n"); + } + else + { + return STATUS_SUCCESS; + } + DPRINT("Reiterating\n"); + ResendIterations--; + } while (ResendIterations); + + return STATUS_IO_TIMEOUT; +} + +/* + * FUNCTION: Write data to a port, waiting first for it to become ready + */ +BOOLEAN +i8042Write( + IN PPORT_DEVICE_EXTENSION DeviceExtension, + IN PUCHAR addr, + IN UCHAR data) +{ + ULONG ResendIterations; + + ASSERT(addr); + ASSERT(DeviceExtension->ControlPort != NULL); + + ResendIterations = DeviceExtension->Settings.ResendIterations; + + while ((KBD_IBF & READ_PORT_UCHAR(DeviceExtension->ControlPort)) && + (ResendIterations--)) + { + KeStallExecutionProcessor(50); + } + + if (ResendIterations) + { + WRITE_PORT_UCHAR(addr, data); + DPRINT("Sent 0x%x to port 0x%x\n", data, addr); + return TRUE; + } + return FALSE; +} diff --git a/reactos/drivers/input/i8042prt/registry.c b/reactos/drivers/input/i8042prt/registry.c index 8bad8ef14b7..2897f6a6228 100644 --- a/reactos/drivers/input/i8042prt/registry.c +++ b/reactos/drivers/input/i8042prt/registry.c @@ -1,211 +1,231 @@ /* - * 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 + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/i8042prt.c + * PURPOSE: Reading the registry + * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) + Copyright Jason Filby (jasonfilby@yahoo.com) + Copyright Martijn Vernooij (o112w8r02@sneakemail.com) + Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) */ -/* INCLUDES ****************************************************************/ +/* INCLUDES ******************************************************************/ #include "i8042prt.h" -#ifndef NDEBUG -#define NDEBUG -#endif -#include - /* 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) - +NTSTATUS +ReadRegistryEntries( + IN PUNICODE_STRING RegistryPath, + OUT PI8042_SETTINGS Settings) { - RTL_QUERY_REGISTRY_TABLE Parameters[19]; - + RTL_QUERY_REGISTRY_TABLE Parameters[17]; NTSTATUS Status; - ULONG DefaultHeadless = 0; - ULONG DefaultCrashScroll = 0; - ULONG DefaultCrashSysRq = 0; - ULONG DefaultReportResetErrors = 0; + ULONG DefaultKeyboardDataQueueSize = 0x64; + PCWSTR DefaultKeyboardDeviceBaseName = L"KeyboardPort"; + ULONG DefaultMouseDataQueueSize = 0x64; + ULONG DefaultMouseResolution = 3; + ULONG DefaultMouseSynchIn100ns = 20000000; + ULONG DefaultNumberOfButtons = 2; + PCWSTR DefaultPointerDeviceBaseName = L"PointerPort"; ULONG DefaultPollStatusIterations = 1; - ULONG DefaultResendIterations = 3; + ULONG DefaultOverrideKeyboardType = 4; + ULONG DefaultOverrideKeyboardSubtype = 0; 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 DefaultResendIterations = 0x3; ULONG DefaultSampleRate = 60; - ULONG DefaultNumberOfButtons = 2; - ULONG DefaultEnableWheelDetection = 1; + ULONG DefaultCrashOnCtrlScroll; + + /* Default value for CrashOnCtrlScroll depends if we're + * running a debug build or a normal build. + */ +#ifdef DBG + DefaultCrashOnCtrlScroll = 1; +#else + DefaultCrashOnCtrlScroll = 0; +#endif 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[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + Parameters[0].Name = L"Parameters"; - Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT; - Parameters[1].Name = L"CrashOnCtrlScroll"; - Parameters[1].EntryContext = &DevExt->Settings.CrashScroll; + Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[1].Name = L"KeyboardDataQueueSize"; + Parameters[1].EntryContext = &Settings->KeyboardDataQueueSize; Parameters[1].DefaultType = REG_DWORD; - Parameters[1].DefaultData = &DefaultCrashScroll; + Parameters[1].DefaultData = &DefaultKeyboardDataQueueSize; 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[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[2].Name = L"KeyboardDeviceBaseName"; + Parameters[2].EntryContext = &Settings->KeyboardDeviceBaseName; + Parameters[2].DefaultType = REG_SZ; + Parameters[2].DefaultData = (PVOID)DefaultKeyboardDeviceBaseName; + Parameters[2].DefaultLength = 0; - Parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT; - Parameters[3].Name = L"ReportResetErrors"; - Parameters[3].EntryContext = &DevExt->Settings.ReportResetErrors; + Parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[3].Name = L"MouseDataQueueSize"; + Parameters[3].EntryContext = &Settings->MouseDataQueueSize; Parameters[3].DefaultType = REG_DWORD; - Parameters[3].DefaultData = &DefaultReportResetErrors; + Parameters[3].DefaultData = &DefaultMouseDataQueueSize; 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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[4].Name = L"MouseResolution"; + Parameters[4].EntryContext = &Settings->MouseResolution; Parameters[4].DefaultType = REG_DWORD; - Parameters[4].DefaultData = &DefaultPollStatusIterations; + Parameters[4].DefaultData = &DefaultMouseResolution; 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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[5].Name = L"MouseSynchIn100ns"; + Parameters[5].EntryContext = &Settings->MouseSynchIn100ns; Parameters[5].DefaultType = REG_DWORD; - Parameters[5].DefaultData = &DefaultResendIterations; + Parameters[5].DefaultData = &DefaultMouseSynchIn100ns; 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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[6].Name = L"NumberOfButtons"; + Parameters[6].EntryContext = &Settings->NumberOfButtons; Parameters[6].DefaultType = REG_DWORD; - Parameters[6].DefaultData = &DefaultPollingIterations; + Parameters[6].DefaultData = &DefaultNumberOfButtons; 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[7].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[7].Name = L"PointerDeviceBaseName"; + Parameters[7].EntryContext = &Settings->PointerDeviceBaseName; + Parameters[7].DefaultType = REG_SZ; + Parameters[7].DefaultData = (PVOID)DefaultPointerDeviceBaseName; + Parameters[7].DefaultLength = 0; - Parameters[8].Flags = RTL_QUERY_REGISTRY_DIRECT; - Parameters[8].Name = L"KeyboardDataQueueSize"; - Parameters[8].EntryContext = - &DevExt->KeyboardAttributes.InputDataQueueLength; + Parameters[8].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[8].Name = L"PollStatusIterations"; + Parameters[8].EntryContext = &Settings->PollStatusIterations; Parameters[8].DefaultType = REG_DWORD; - Parameters[8].DefaultData = &DefaultKeyboardDataQueueSize; + Parameters[8].DefaultData = &DefaultPollStatusIterations; Parameters[8].DefaultLength = sizeof(ULONG); - Parameters[9].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[9].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; Parameters[9].Name = L"OverrideKeyboardType"; - Parameters[9].EntryContext = &DevExt->Settings.OverrideKeyboardType; + Parameters[9].EntryContext = &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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; Parameters[10].Name = L"OverrideKeyboardSubtype"; - Parameters[10].EntryContext = &DevExt->Settings.OverrideKeyboardSubtype; + Parameters[10].EntryContext = &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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[11].Name = L"PollingIterations"; + Parameters[11].EntryContext = &Settings->PollingIterations; Parameters[11].DefaultType = REG_DWORD; - Parameters[11].DefaultData = &DefaultMouseDataQueueSize; + Parameters[11].DefaultData = &DefaultPollingIterations; 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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[12].Name = L"PollingIterationsMaximum"; + Parameters[12].EntryContext = &Settings->PollingIterationsMaximum; Parameters[12].DefaultType = REG_DWORD; - Parameters[12].DefaultData = &DefaultMouseResendStallTime; + Parameters[12].DefaultData = &DefaultPollingIterationsMaximum; 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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[13].Name = L"ResendIterations"; + Parameters[13].EntryContext = &Settings->ResendIterations; Parameters[13].DefaultType = REG_DWORD; - Parameters[13].DefaultData = &DefaultMouseSynchIn100ns; + Parameters[13].DefaultData = &DefaultResendIterations; 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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[14].Name = L"SampleRate"; + Parameters[14].EntryContext = &Settings->SampleRate; Parameters[14].DefaultType = REG_DWORD; - Parameters[14].DefaultData = &DefaultMouseResolution; + Parameters[14].DefaultData = &DefaultSampleRate; 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].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; + Parameters[15].Name = L"CrashOnCtrlScroll"; + Parameters[15].EntryContext = &Settings->CrashOnCtrlScroll; Parameters[15].DefaultType = REG_DWORD; - Parameters[15].DefaultData = &DefaultSampleRate; + Parameters[15].DefaultData = &DefaultCrashOnCtrlScroll; 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); + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + RegistryPath->Buffer, + Parameters, + NULL, + NULL); - 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; - DPRINT ("Can't read registry: %x\n", Status); - while (Current->Name) { - *((PULONG)Current->EntryContext) = - *((PULONG)Current->DefaultData); - Current++; + if (NT_SUCCESS(Status)) + { + /* Check values */ + if (Settings->KeyboardDataQueueSize < 1) + Settings->KeyboardDataQueueSize = DefaultKeyboardDataQueueSize; + if (Settings->MouseDataQueueSize < 1) + Settings->MouseDataQueueSize = DefaultMouseDataQueueSize; + if (Settings->NumberOfButtons < 1) + Settings->NumberOfButtons = DefaultNumberOfButtons; + if (Settings->PollingIterations < 0x400) + Settings->PollingIterations = DefaultPollingIterations; + if (Settings->PollingIterationsMaximum < 0x400) + Settings->PollingIterationsMaximum = DefaultPollingIterationsMaximum; + if (Settings->ResendIterations < 1) + Settings->ResendIterations = DefaultResendIterations; + } + else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + { + /* Registry path doesn't exist. Set defaults */ + Settings->KeyboardDataQueueSize = DefaultKeyboardDataQueueSize; + Settings->MouseDataQueueSize = DefaultMouseDataQueueSize; + Settings->MouseResolution = DefaultMouseResolution; + Settings->MouseSynchIn100ns = DefaultMouseSynchIn100ns; + Settings->NumberOfButtons = DefaultNumberOfButtons; + Settings->PollStatusIterations = DefaultPollStatusIterations; + Settings->OverrideKeyboardType = DefaultOverrideKeyboardType; + Settings->OverrideKeyboardSubtype = DefaultOverrideKeyboardSubtype; + Settings->PollingIterations = DefaultPollingIterations; + Settings->PollingIterationsMaximum = DefaultPollingIterationsMaximum; + Settings->ResendIterations = DefaultResendIterations; + Settings->SampleRate = DefaultSampleRate; + Settings->CrashOnCtrlScroll = DefaultCrashOnCtrlScroll; + if (!RtlCreateUnicodeString(&Settings->KeyboardDeviceBaseName, DefaultKeyboardDeviceBaseName) + || !RtlCreateUnicodeString(&Settings->PointerDeviceBaseName, DefaultPointerDeviceBaseName)) + { + DPRINT("RtlCreateUnicodeString() failed\n"); + Status = STATUS_NO_MEMORY; + } + else + { + Status = STATUS_SUCCESS; } - DPRINT ("Manually set defaults\n"); - } - if (DevExt->Settings.MouseResolution > 3) - DevExt->Settings.MouseResolution = 3; + if (NT_SUCCESS(Status)) + { + DPRINT("KeyboardDataQueueSize : 0x%lx\n", Settings->KeyboardDataQueueSize); + DPRINT("KeyboardDeviceBaseName : %wZ\n", &Settings->KeyboardDeviceBaseName); + DPRINT("MouseDataQueueSize : 0x%lx\n", Settings->MouseDataQueueSize); + DPRINT("MouseResolution : 0x%lx\n", Settings->MouseResolution); + DPRINT("MouseSynchIn100ns : %lu\n", Settings->MouseSynchIn100ns); + DPRINT("NumberOfButtons : 0x%lx\n", Settings->NumberOfButtons); + DPRINT("PointerDeviceBaseName : %wZ\n", &Settings->PointerDeviceBaseName); + DPRINT("PollStatusIterations : 0x%lx\n", Settings->PollStatusIterations); + DPRINT("OverrideKeyboardType : 0x%lx\n", Settings->OverrideKeyboardType); + DPRINT("OverrideKeyboardSubtype : 0x%lx\n", Settings->OverrideKeyboardSubtype); + DPRINT("PollingIterations : 0x%lx\n", Settings->PollingIterations); + DPRINT("PollingIterationsMaximum : %lu\n", Settings->PollingIterationsMaximum); + DPRINT("ResendIterations : 0x%lx\n", Settings->ResendIterations); + DPRINT("SampleRate : %lu\n", Settings->SampleRate); + } - DPRINT("Done reading registry\n"); + return Status; } diff --git a/reactos/drivers/input/i8042prt/setup.c b/reactos/drivers/input/i8042prt/setup.c new file mode 100644 index 00000000000..baad79e1d82 --- /dev/null +++ b/reactos/drivers/input/i8042prt/setup.c @@ -0,0 +1,273 @@ +/* + * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/input/i8042prt/setup.c + * PURPOSE: Create a legacy PDO during ReactOS installation + * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +/* NOTE: + * All this file is a big hack and should be removed one day... + */ + +/* INCLUDES ******************************************************************/ + +#include "i8042prt.h" + +/* GLOBALS *******************************************************************/ + +#define KEYBOARD_DATA_PORT 0x60 +#define KEYBOARD_CONTROL_PORT 0x64 +#define KEYBOARD_IRQ 1 + +/* FUNCTIONS *****************************************************************/ + +BOOLEAN +IsFirstStageSetup( + VOID) +{ + UNICODE_STRING PathU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\Setup"); + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE hSetupKey = (HANDLE)NULL; + NTSTATUS Status; + BOOLEAN ret = TRUE; + + InitializeObjectAttributes(&ObjectAttributes, &PathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + Status = ZwOpenKey(&hSetupKey, KEY_QUERY_VALUE, &ObjectAttributes); + + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + ret = TRUE; + else + ret = FALSE; + + if (hSetupKey != (HANDLE)NULL) + ZwClose(hSetupKey); + DPRINT("IsFirstStageSetup() returns %s\n", ret ? "YES" : "NO"); + return ret; +} + +static VOID NTAPI +SendStartDevice( + IN PDRIVER_OBJECT DriverObject, + IN PVOID Context, + IN ULONG Count) +{ + PDEVICE_OBJECT Pdo; + PCM_RESOURCE_LIST AllocatedResources = NULL; + PCM_RESOURCE_LIST AllocatedResourcesTranslated = NULL; + PDEVICE_OBJECT TopDeviceObject = NULL; + KEVENT Event; + IO_STATUS_BLOCK IoStatusBlock; + PIRP Irp; + PIO_STACK_LOCATION Stack; + ULONG ResourceListSize; + NTSTATUS Status; + + Pdo = (PDEVICE_OBJECT)Context; + DPRINT("SendStartDevice(%p)\n", Pdo); + + /* Create default resource list */ + ResourceListSize = sizeof(CM_RESOURCE_LIST) + 3 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); + AllocatedResources = ExAllocatePoolWithTag(PagedPool, ResourceListSize, I8042PRT_TAG); + if (!AllocatedResources) + { + DPRINT("ExAllocatePoolWithTag() failed\n"); + Status = STATUS_NO_MEMORY; + goto cleanup; + } + AllocatedResources->Count = 1; + AllocatedResources->List[0].PartialResourceList.Version = 1; + AllocatedResources->List[0].PartialResourceList.Revision = 1; + AllocatedResources->List[0].PartialResourceList.Count = 3; + /* Data port */ + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[0].Type = CmResourceTypePort; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[0].ShareDisposition = CmResourceShareDeviceExclusive; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[0].Flags = 0; /* FIXME */ + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[0].u.Port.Start.u.HighPart = 0; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[0].u.Port.Start.u.LowPart = KEYBOARD_DATA_PORT; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[0].u.Port.Length = 1; + /* Control port */ + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[1].Type = CmResourceTypePort; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[1].ShareDisposition = CmResourceShareDeviceExclusive; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[1].Flags = 0; /* FIXME */ + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[1].u.Port.Start.u.HighPart = 0; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[1].u.Port.Start.u.LowPart = KEYBOARD_CONTROL_PORT; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[1].u.Port.Length = 1; + /* Interrupt */ + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].Type = CmResourceTypeInterrupt; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].ShareDisposition = CmResourceShareDeviceExclusive; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].Flags = CM_RESOURCE_INTERRUPT_LATCHED; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Level = KEYBOARD_IRQ; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Vector = 0; + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Affinity = (KAFFINITY)-1; + + /* Create default resource list translated */ + AllocatedResourcesTranslated = ExAllocatePoolWithTag(PagedPool, ResourceListSize, I8042PRT_TAG); + if (!AllocatedResourcesTranslated) + { + DPRINT("ExAllocatePoolWithTag() failed\n"); + Status = STATUS_NO_MEMORY; + goto cleanup; + } + RtlCopyMemory(AllocatedResourcesTranslated, AllocatedResources, ResourceListSize); + AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Vector = HalGetInterruptVector( + Internal, 0, + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Level, + AllocatedResources->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Vector, + (PKIRQL)&AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Level, + &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[2].u.Interrupt.Affinity); + + /* Send IRP_MN_START_DEVICE */ + TopDeviceObject = IoGetAttachedDeviceReference(Pdo); + KeInitializeEvent( + &Event, + NotificationEvent, + FALSE); + Irp = IoBuildSynchronousFsdRequest( + IRP_MJ_PNP, + TopDeviceObject, + NULL, + 0, + NULL, + &Event, + &IoStatusBlock); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + Irp->IoStatus.Information = 0; + Stack = IoGetNextIrpStackLocation(Irp); + Stack->MinorFunction = IRP_MN_START_DEVICE; + Stack->Parameters.StartDevice.AllocatedResources = AllocatedResources; + Stack->Parameters.StartDevice.AllocatedResourcesTranslated = AllocatedResourcesTranslated; + Status = IoCallDriver(TopDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject( + &Event, + Executive, + KernelMode, + FALSE, + NULL); + Status = IoStatusBlock.Status; + } + if (!NT_SUCCESS(Status)) + { + DPRINT("IoCallDriver() failed with status 0x%08lx\n", Status); + goto cleanup; + } + +cleanup: + if (TopDeviceObject) + ObDereferenceObject(TopDeviceObject); + if (AllocatedResources) + ExFreePoolWithTag(AllocatedResources, I8042PRT_TAG); + if (AllocatedResourcesTranslated) + ExFreePoolWithTag(AllocatedResourcesTranslated, I8042PRT_TAG); +} + +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; +} + +NTSTATUS +i8042AddLegacyKeyboard( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath) +{ + UNICODE_STRING KeyboardName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardPort8042"); + PI8042_DEVICE_TYPE DeviceExtension = NULL; + PDEVICE_OBJECT Pdo = NULL; + NTSTATUS Status; + + DPRINT("i8042AddLegacyKeyboard()\n"); + + /* Create a named PDO */ + Status = IoCreateDevice( + DriverObject, + sizeof(I8042_DEVICE_TYPE), + &KeyboardName, + FILE_DEVICE_8042_PORT, + FILE_DEVICE_SECURE_OPEN, + TRUE, + &Pdo); + if (!NT_SUCCESS(Status)) + { + DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status); + goto cleanup; + } + + /* Initialize device extension */ + DeviceExtension = (PI8042_DEVICE_TYPE)Pdo->DeviceExtension; + RtlZeroMemory(DeviceExtension, sizeof(I8042_DEVICE_TYPE)); + *DeviceExtension = PhysicalDeviceObject; + Pdo->Flags &= ~DO_DEVICE_INITIALIZING; + + /* Add FDO at the top of the PDO */ + Status = i8042AddDevice(DriverObject, Pdo); + if (!NT_SUCCESS(Status)) + { + DPRINT("i8042AddDevice() failed with status 0x%08lx\n", Status); + goto cleanup; + } + + /* We will send the IRP_MN_START_DEVICE later, once kbdclass is loaded */ + AddRegistryEntry(L"KeyboardPort", &KeyboardName, RegistryPath->Buffer); + IoRegisterBootDriverReinitialization( + DriverObject, + SendStartDevice, + Pdo); + + Status = STATUS_SUCCESS; + /* Yes, completly forget the Pdo pointer, as we will never + * have to unload this driver during first stage setup. + */ + +cleanup: + if (!NT_SUCCESS(Status)) + { + if (Pdo) + IoDeleteDevice(Pdo); + } + return Status; +}