Copy i8042prt driver from 0.3.1 branch to trunk

svn path=/trunk/; revision=24377
This commit is contained in:
Hervé Poussineau 2006-10-03 18:00:04 +00:00
commit 273908b19f
9 changed files with 3479 additions and 0 deletions

View file

@ -0,0 +1,83 @@
Intel 8042 port driver
This directory contains a driver for Intels 8042 and compatible controllers.
It is based on the information in the DDK documentation on MSDN. It is intended
to be compatible with keyboard and mouse drivers written for Windows. It is
not based on the i8042prt example driver that's included with the DDK.
The directory contains these files:
i8042prt.c: Main controller functionality, things shared by keyboards and mice
keyboard.c: keyboard functionality: detection, interrupt handling
mouse.c: mouse functionality: detection, interrupt handling, packet parsing for
standard ps2 and microsoft mice
ps2pp.c: logitech ps2++ mouse packat parsing (basic)
registry.c: registry reading
makefile, i8042prt.rc: obvious
Some parts of the driver make little sense. This is because it implements
an interface that has evolved over a long time, and because the ps/2
'standard' is really awful.
Things to add:
- Better AT (before ps2) keyboard handling
- SiS keyboard controller detection
- Mouse identification
- General robustness: reset mouse if things go wrong
- Handling all registry settings
- ACPI
- Make it work more like a WDM driver
Things not to add:
- Other mouse protocols, touchpad handling etc. : Write a filter driver instead
- Keyboard lights handling: Should be in win32k
- Keyboard scancode translation: Should be in win32k
Things requiring work elsewhere:
- Debugger interface (TAB + key):
Currently this interface wants translated keycodes, which are not
implemented by this driver. As it just uses a giant switch with
hardcoded cases, this should not be hard to fix.
- Class drivers:
I wrote a keyboard class driver, which does keycode translations. It
should not do this, win32k should get untranslated keycodes and do
the translation itself.
I changed the mouse class driver (mouclass) to work like Microsofts mouclass.
Unfortunately this means that the original psaux driver doesn't work
anymore (the same holds for the other mice drivers, probably).
The keyboard class driver passes on ioctls from win32k, so it can change
keyboard settings. As far as I could see, the mouse class driver does not
do this yet.
The class drivers should be able to handle reads for more than one packet
at a time (kbdclass should, mouclass does not). Win32k should send such
requests.
I put a lot of work in making it work like Microsofts driver does, so third party drivers can work. Please keep it that way.
Links:
Here's a link describing most of the registry settings:
http://www.microsoft.com/resources/documentation/Windows/2000/server/reskit/en-us/Default.asp?url=/resources/documentation/Windows/2000/server/reskit/en-us/regentry/31493.asp
PS/2 protocol documentation:
http://www.win.tue.nl/~aeb/linux/kbd/scancodes.html
It also contains a link to a description of the ps2++ protocol, which has
since disappeared. Archive.org still has it.

View file

@ -0,0 +1,927 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/i8042prt.c
* PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
* PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
* Jason Filby (jasonfilby@yahoo.com)
* Tinus
*/
/* INCLUDES ****************************************************************/
#define NDEBUG
#include <debug.h>
#include "i8042prt.h"
/* GLOBALS *******************************************************************/
/*
* Driver data
*/
#define I8042_TIMEOUT 500000
#define I8042_MAX_COMMAND_LENGTH 16
#define I8042_MAX_UPWARDS_STACK 5
UNICODE_STRING I8042RegistryPath;
/* FUNCTIONS *****************************************************************/
/*
* FUNCTION: Write data to a port, waiting first for it to become ready
*/
BOOLEAN I8042Write(PDEVICE_EXTENSION DevExt, PUCHAR addr, UCHAR data)
{
ULONG ResendIterations = DevExt->Settings.PollingIterations;
while ((KBD_IBF & READ_PORT_UCHAR(I8042_CTRL_PORT)) &&
(ResendIterations--))
{
KeStallExecutionProcessor(50);
}
if (ResendIterations) {
WRITE_PORT_UCHAR(addr,data);
DPRINT("Sent %x to %x\n", data, addr);
return TRUE;
}
return FALSE;
}
#if 0 /* function is not needed */
/*
* FUNCTION: Write data to a port, without waiting first
*/
static BOOLEAN I8042WriteNoWait(PDEVICE_EXTENSION DevExt, int addr, UCHAR data)
{
WRITE_PORT_UCHAR((PUCHAR)addr,data);
DPRINT("Sent %x to %x\n", data, addr);
return TRUE;
}
#endif
/*
* FUNCTION: Read data from port 0x60
*/
NTSTATUS I8042ReadData(UCHAR *Data)
{
UCHAR Status;
Status=READ_PORT_UCHAR(I8042_CTRL_PORT);
// If data is available
if ((Status & KBD_OBF)) {
Data[0]=READ_PORT_UCHAR((PUCHAR)I8042_DATA_PORT);
DPRINT("Read: %x (status: %x)\n", Data[0], Status);
// If the data is valid (not timeout, not parity error)
if (0 == (Status & KBD_PERR))
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
NTSTATUS I8042ReadStatus(UCHAR *Status)
{
Status[0]=READ_PORT_UCHAR(I8042_CTRL_PORT);
return STATUS_SUCCESS;
}
/*
* FUNCTION: Read data from port 0x60
*/
NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data)
{
ULONG Counter = DevExt->Settings.PollingIterations;
NTSTATUS Status;
while (Counter--) {
Status = I8042ReadData(Data);
if (STATUS_SUCCESS == Status)
return Status;
KeStallExecutionProcessor(50);
}
// Timed out
return STATUS_IO_TIMEOUT;
}
VOID I8042Flush()
{
UCHAR Ignore;
while (STATUS_SUCCESS == I8042ReadData(&Ignore)) {
DPRINT("Data flushed\n"); /* drop */
}
}
VOID STDCALL I8042IsrWritePort(PDEVICE_EXTENSION DevExt,
UCHAR Value,
UCHAR SelectCmd)
{
if (SelectCmd)
if (!I8042Write(DevExt, I8042_CTRL_PORT, SelectCmd))
return;
I8042Write(DevExt, I8042_DATA_PORT, Value);
}
/*
* These functions are callbacks for filter driver custom
* initialization routines.
*/
NTSTATUS STDCALL I8042SynchWritePort(PDEVICE_EXTENSION DevExt,
UCHAR Port,
UCHAR Value,
BOOLEAN WaitForAck)
{
NTSTATUS Status;
UCHAR Ack;
ULONG ResendIterations = DevExt->Settings.ResendIterations + 1;
do {
if (Port)
if (!I8042Write(DevExt, I8042_DATA_PORT, Port))
{
DPRINT1("Failed to write Port\n");
return STATUS_IO_TIMEOUT;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value))
{
DPRINT1("Failed to write Value\n");
return STATUS_IO_TIMEOUT;
}
if (WaitForAck) {
Status = I8042ReadDataWait(DevExt, &Ack);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to read Ack\n");
return Status;
}
if (Ack == KBD_ACK)
return STATUS_SUCCESS;
if (Ack != KBD_RESEND)
{
DPRINT1("Unexpected Ack 0x%x\n", Ack);
return STATUS_UNEXPECTED_IO_ERROR;
}
} else {
return STATUS_SUCCESS;
}
DPRINT("Reiterating\n");
ResendIterations--;
} while (ResendIterations);
return STATUS_IO_TIMEOUT;
}
/*
* This one reads a value from the port; You don't have to specify
* which one, it'll always be from the one you talked to, so one function
* is enough this time. Note how MSDN specifies the
* WaitForAck parameter to be ignored.
*/
static NTSTATUS STDCALL I8042SynchReadPort(PVOID Context,
PUCHAR Value,
BOOLEAN WaitForAck)
{
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)Context;
return I8042ReadDataWait(DevExt, Value);
}
/* Write the current byte of the packet. Returns FALSE in case
* of problems.
*/
static BOOLEAN STDCALL I8042PacketWrite(PDEVICE_EXTENSION DevExt)
{
UCHAR Port = DevExt->PacketPort;
if (Port) {
if (!I8042Write(DevExt,
I8042_CTRL_PORT,
Port)) {
/* something is really wrong! */
DPRINT1("Failed to send packet byte!\n");
return FALSE;
}
}
return I8042Write(DevExt,
I8042_DATA_PORT,
DevExt->Packet.Bytes[DevExt->Packet.CurrentByte]);
}
/*
* This function starts a packet. It must be called with the
* correct DIRQL.
*/
NTSTATUS STDCALL I8042StartPacket(PDEVICE_EXTENSION DevExt,
PDEVICE_OBJECT Device,
PUCHAR Bytes,
ULONG ByteCount,
PIRP Irp)
{
KIRQL Irql;
NTSTATUS Status;
PFDO_DEVICE_EXTENSION FdoDevExt = Device->DeviceExtension;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->CurrentIrp = Irp;
DevExt->CurrentIrpDevice = Device;
if (Idle != DevExt->Packet.State) {
Status = STATUS_DEVICE_BUSY;
goto startpacketdone;
}
DevExt->Packet.Bytes = Bytes;
DevExt->Packet.CurrentByte = 0;
DevExt->Packet.ByteCount = ByteCount;
DevExt->Packet.State = SendingBytes;
DevExt->PacketResult = Status = STATUS_PENDING;
if (Mouse == FdoDevExt->Type)
DevExt->PacketPort = 0xD4;
else
DevExt->PacketPort = 0;
if (!I8042PacketWrite(DevExt)) {
Status = STATUS_IO_TIMEOUT;
DevExt->Packet.State = Idle;
DevExt->PacketResult = STATUS_ABANDONED;
goto startpacketdone;
}
DevExt->Packet.CurrentByte++;
startpacketdone:
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
if (STATUS_PENDING != Status) {
DevExt->CurrentIrp = NULL;
DevExt->CurrentIrpDevice = NULL;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return Status;
}
BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt,
UCHAR Output)
{
if (Idle == DevExt->Packet.State)
return FALSE;
switch (Output) {
case KBD_RESEND:
DevExt->PacketResends++;
if (DevExt->PacketResends > DevExt->Settings.ResendIterations) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_IO_TIMEOUT;
DevExt->PacketResends = 0;
return TRUE;
}
DevExt->Packet.CurrentByte--;
break;
case KBD_NACK:
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_UNEXPECTED_IO_ERROR;
DevExt->PacketResends = 0;
return TRUE;
default:
DevExt->PacketResends = 0;
}
if (DevExt->Packet.CurrentByte >= DevExt->Packet.ByteCount) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_SUCCESS;
return TRUE;
}
if (!I8042PacketWrite(DevExt)) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_IO_TIMEOUT;
return TRUE;
}
DevExt->Packet.CurrentByte++;
return TRUE;
}
VOID I8042PacketDpc(PDEVICE_EXTENSION DevExt)
{
BOOLEAN FinishIrp = FALSE;
NTSTATUS Result = STATUS_INTERNAL_ERROR; /* Shouldn't happen */
KIRQL Irql;
/* If the interrupt happens before this is setup, the key
* was already in the buffer. Too bad! */
if (!DevExt->HighestDIRQLInterrupt)
return;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
if (Idle == DevExt->Packet.State &&
DevExt->PacketComplete) {
FinishIrp = TRUE;
Result = DevExt->PacketResult;
DevExt->PacketComplete = FALSE;
}
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt,
Irql);
if (!FinishIrp)
return;
if (DevExt->CurrentIrp) {
DevExt->CurrentIrp->IoStatus.Status = Result;
IoCompleteRequest(DevExt->CurrentIrp, IO_NO_INCREMENT);
IoStartNextPacket(DevExt->CurrentIrpDevice, FALSE);
DevExt->CurrentIrp = NULL;
DevExt->CurrentIrpDevice = NULL;
}
}
VOID STDCALL I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject,
PVOID Context)
{
KEVENT Event;
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status;
PDEVICE_EXTENSION DevExt;
PFDO_DEVICE_EXTENSION FdoDevExt;
PIRP NewIrp;
PI8042_HOOK_WORKITEM WorkItemData = (PI8042_HOOK_WORKITEM)Context;
ULONG IoControlCode;
PVOID InputBuffer;
ULONG InputBufferLength;
BOOLEAN IsKbd;
DPRINT("HookWorkItem\n");
FdoDevExt = (PFDO_DEVICE_EXTENSION)
DeviceObject->DeviceExtension;
DevExt = FdoDevExt->PortDevExt;
if (WorkItemData->Target == DevExt->KeyboardData.ClassDeviceObject) {
IoControlCode = IOCTL_INTERNAL_I8042_HOOK_KEYBOARD;
InputBuffer = &DevExt->KeyboardHook;
InputBufferLength = sizeof(INTERNAL_I8042_HOOK_KEYBOARD);
IsKbd = TRUE;
DPRINT ("is for keyboard.\n");
} else if (WorkItemData->Target == DevExt->MouseData.ClassDeviceObject){
IoControlCode = IOCTL_INTERNAL_I8042_HOOK_MOUSE;
InputBuffer = &DevExt->MouseHook;
InputBufferLength = sizeof(INTERNAL_I8042_HOOK_MOUSE);
IsKbd = FALSE;
DPRINT ("is for mouse.\n");
} else {
DPRINT1("I8042SendHookWorkItem: Can't find DeviceObject\n");
WorkItemData->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
goto hookworkitemdone;
}
KeInitializeEvent(&Event, NotificationEvent, FALSE);
NewIrp = IoBuildDeviceIoControlRequest(
IoControlCode,
WorkItemData->Target,
InputBuffer,
InputBufferLength,
NULL,
0,
TRUE,
&Event,
&IoStatus);
if (!NewIrp) {
DPRINT("IOCTL_INTERNAL_(device)_CONNECT: "
"Can't allocate IRP\n");
WorkItemData->Irp->IoStatus.Status =
STATUS_INSUFFICIENT_RESOURCES;
goto hookworkitemdone;
}
#if 0
Status = IoCallDriver(
WorkItemData->Target,
NewIrp);
if (STATUS_PENDING == Status)
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
#endif
if (IsKbd) {
/* Call the hooked initialization if it exists */
if (DevExt->KeyboardHook.InitializationRoutine) {
Status = DevExt->KeyboardHook.InitializationRoutine(
DevExt->KeyboardHook.Context,
DevExt,
I8042SynchReadPort,
I8042SynchWritePortKbd,
FALSE);
if (!NT_SUCCESS(Status)) {
WorkItemData->Irp->IoStatus.Status = Status;
goto hookworkitemdone;
}
}
/* TODO: Now would be the right time to enable the interrupt */
DevExt->KeyboardClaimed = TRUE;
} else {
/* Mouse doesn't have this, but we need to send a
* reset to start the detection.
*/
KIRQL Irql;
Irql = KeAcquireInterruptSpinLock(
DevExt->HighestDIRQLInterrupt);
I8042Write(DevExt, I8042_CTRL_PORT, 0xD4);
I8042Write(DevExt, I8042_DATA_PORT, 0xFF);
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
}
WorkItemData->Irp->IoStatus.Status = STATUS_SUCCESS;
hookworkitemdone:
WorkItemData->Irp->IoStatus.Information = 0;
IoCompleteRequest(WorkItemData->Irp, IO_NO_INCREMENT);
IoFreeWorkItem(WorkItemData->WorkItem);
ExFreePool(WorkItemData);
DPRINT("HookWorkItem done\n");
}
static VOID STDCALL I8042StartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
if (!I8042StartIoKbd(DeviceObject, Irp)) {
DPRINT1("Unhandled StartIo!\n");
}
}
static NTSTATUS STDCALL I8042InternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
DPRINT("InternalDeviceControl\n");
switch (FdoDevExt->Type) {
case Keyboard:
Status = I8042InternalDeviceControlKbd(DeviceObject, Irp);
break;
case Mouse:
Status = I8042InternalDeviceControlMouse(DeviceObject, Irp);
break;
}
if (Status == STATUS_INVALID_DEVICE_REQUEST) {
DPRINT1("Invalid internal device request!\n");
}
if (Status != STATUS_PENDING)
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
static NTSTATUS STDCALL I8042CreateDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS Status;
DPRINT ("I8042CreateDispatch\n");
Status = STATUS_SUCCESS;
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
static NTSTATUS STDCALL I8042BasicDetect(PDEVICE_EXTENSION DevExt)
{
NTSTATUS Status;
UCHAR Value = 0;
ULONG Counter;
DevExt->MouseExists = FALSE;
DevExt->KeyboardExists = FALSE;
I8042Flush();
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST)) {
DPRINT1("Writing KBD_SELF_TEST command failed\n");
return STATUS_IO_TIMEOUT;
}
// Wait longer?
Counter = 10;
do {
Status = I8042ReadDataWait(DevExt, &Value);
} while ((Counter--) && (STATUS_IO_TIMEOUT == Status));
if (!NT_SUCCESS(Status)) {
DPRINT1("Failed to read KBD_SELF_TEST response, status 0x%x\n",
Status);
return Status;
}
if (Value != 0x55) {
DPRINT1("Got %x instead of 55\n", Value);
return STATUS_IO_DEVICE_ERROR;
}
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value |= CCB_KBD_DISAB | CCB_MOUSE_DISAB; /* disable keyboard/mouse */
Value &= ~(CCB_KBD_INT_ENAB | CCB_MOUSE_INT_ENAB);
/* don't enable keyboard and mouse interrupts */
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
/*
* We used to send a KBD_LINE_TEST command here, but on at least HP
* Pavilion notebooks the response to that command was incorrect.
* So now we just assume that a keyboard is attached.
*/
DevExt->KeyboardExists = TRUE;
if (I8042Write(DevExt, I8042_CTRL_PORT, MOUSE_LINE_TEST))
{
Status = I8042ReadDataWait(DevExt, &Value);
if (NT_SUCCESS(Status) && Value == 0)
DevExt->MouseExists = TRUE;
}
return STATUS_SUCCESS;
}
static NTSTATUS STDCALL I8042Initialize(PDEVICE_EXTENSION DevExt)
{
NTSTATUS Status;
Status = I8042BasicDetect(DevExt);
if (!NT_SUCCESS(Status)) {
DPRINT1("Basic keyboard detection failed: %x\n", Status);
return Status;
}
if (DevExt->MouseExists) {
DPRINT("Aux port detected\n");
DevExt->MouseExists = I8042DetectMouse(DevExt);
}
if (!DevExt->KeyboardExists) {
DPRINT("Keyboard port not detected\n");
if (DevExt->Settings.Headless)
/* Act as if it exists regardless */
DevExt->KeyboardExists = TRUE;
} else {
DPRINT("Keyboard port detected\n");
DevExt->KeyboardExists = I8042DetectKeyboard(DevExt);
}
if (DevExt->KeyboardExists) {
DPRINT("Keyboard detected\n");
I8042KeyboardEnable(DevExt);
I8042KeyboardEnableInterrupt(DevExt);
}
if (DevExt->MouseExists) {
DPRINT("Mouse detected\n");
I8042MouseEnable(DevExt);
}
return STATUS_SUCCESS;
}
static NTSTATUS
AddRegistryEntry(
IN PCWSTR PortTypeName,
IN PUNICODE_STRING DeviceName,
IN PCWSTR RegistryPath)
{
UNICODE_STRING PathU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hDeviceMapKey = (HANDLE)-1;
HANDLE hPortKey = (HANDLE)-1;
UNICODE_STRING PortTypeNameU;
NTSTATUS Status;
InitializeObjectAttributes(&ObjectAttributes, &PathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
goto cleanup;
}
RtlInitUnicodeString(&PortTypeNameU, PortTypeName);
InitializeObjectAttributes(&ObjectAttributes, &PortTypeNameU, OBJ_KERNEL_HANDLE, hDeviceMapKey, NULL);
Status = ZwCreateKey(&hPortKey, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
goto cleanup;
}
Status = ZwSetValueKey(hPortKey, DeviceName, 0, REG_SZ, (PVOID)RegistryPath, wcslen(RegistryPath) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
if (!NT_SUCCESS(Status))
{
DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
goto cleanup;
}
Status = STATUS_SUCCESS;
cleanup:
if (hDeviceMapKey != (HANDLE)-1)
ZwClose(hDeviceMapKey);
if (hPortKey != (HANDLE)-1)
ZwClose(hPortKey);
return Status;
}
static NTSTATUS STDCALL I8042AddDevice(PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT Pdo)
{
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardPort8042");
UNICODE_STRING MouseName = RTL_CONSTANT_STRING(L"\\Device\\PointerPort8042");
ULONG MappedIrqKeyboard = 0, MappedIrqMouse = 0;
KIRQL DirqlKeyboard = 0;
KIRQL DirqlMouse = 0;
KIRQL DirqlMax;
KAFFINITY Affinity;
NTSTATUS Status;
PDEVICE_EXTENSION DevExt;
PFDO_DEVICE_EXTENSION FdoDevExt;
PDEVICE_OBJECT Fdo;
DPRINT("I8042AddDevice\n");
if (Pdo != NULL)
{
/* Device detected by pnpmgr. Ignore it, as we already have
* detected the keyboard and mouse at first call */
return STATUS_UNSUCCESSFUL;
}
Status = IoCreateDevice(DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_8042_PORT,
FILE_DEVICE_SECURE_OPEN,
TRUE,
&Fdo);
if (!NT_SUCCESS(Status))
return Status;
DevExt = Fdo->DeviceExtension;
RtlZeroMemory(DevExt, sizeof(DEVICE_EXTENSION));
I8042ReadRegistry(DriverObject, DevExt);
KeInitializeSpinLock(&DevExt->SpinLock);
InitializeListHead(&DevExt->BusDevices);
KeInitializeDpc(&DevExt->DpcKbd,
I8042DpcRoutineKbd,
DevExt);
KeInitializeDpc(&DevExt->DpcMouse,
I8042DpcRoutineMouse,
DevExt);
KeInitializeDpc(&DevExt->DpcMouseTimeout,
I8042DpcRoutineMouseTimeout,
DevExt);
KeInitializeTimer(&DevExt->TimerMouseTimeout);
Status = I8042Initialize(DevExt);
Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
if (!NT_SUCCESS(STATUS_SUCCESS)) {
DPRINT1("Initialization failure: %x\n", Status);
return Status;
}
if (DevExt->KeyboardExists) {
MappedIrqKeyboard = HalGetInterruptVector(Internal,
0,
0,
KEYBOARD_IRQ,
&DirqlKeyboard,
&Affinity);
Status = IoCreateDevice(DriverObject,
sizeof(FDO_DEVICE_EXTENSION),
&DeviceName,
FILE_DEVICE_8042_PORT,
FILE_DEVICE_SECURE_OPEN,
TRUE,
&Fdo);
if (NT_SUCCESS(Status))
{
AddRegistryEntry(L"KeyboardPort", &DeviceName, L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\i8042prt");
FdoDevExt = Fdo->DeviceExtension;
RtlZeroMemory(FdoDevExt, sizeof(FDO_DEVICE_EXTENSION));
FdoDevExt->PortDevExt = DevExt;
FdoDevExt->Type = Keyboard;
FdoDevExt->DeviceObject = Fdo;
Fdo->Flags |= DO_BUFFERED_IO;
DevExt->DebugWorkItem = IoAllocateWorkItem(Fdo);
DevExt->KeyboardObject = Fdo;
DevExt->KeyboardBuffer = ExAllocatePoolWithTag(
NonPagedPool,
DevExt->KeyboardAttributes.InputDataQueueLength *
sizeof(KEYBOARD_INPUT_DATA),
TAG_I8042);
if (!DevExt->KeyboardBuffer) {
DPRINT1("No memory for keyboardbuffer\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
InsertTailList(&DevExt->BusDevices, &FdoDevExt->BusDevices);
Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
}
else
DevExt->KeyboardExists = FALSE;
}
if (DevExt->MouseExists) {
MappedIrqMouse = HalGetInterruptVector(Internal,
0,
0,
MOUSE_IRQ,
&DirqlMouse,
&Affinity);
Status = IoCreateDevice(DriverObject,
sizeof(FDO_DEVICE_EXTENSION),
&MouseName,
FILE_DEVICE_8042_PORT,
FILE_DEVICE_SECURE_OPEN,
TRUE,
&Fdo);
if (NT_SUCCESS(Status))
{
AddRegistryEntry(L"PointerPort", &MouseName, L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\i8042prt");
FdoDevExt = Fdo->DeviceExtension;
RtlZeroMemory(FdoDevExt, sizeof(FDO_DEVICE_EXTENSION));
FdoDevExt->PortDevExt = DevExt;
FdoDevExt->Type = Mouse;
FdoDevExt->DeviceObject = Fdo;
Fdo->Flags |= DO_BUFFERED_IO;
DevExt->MouseObject = Fdo;
DevExt->MouseBuffer = ExAllocatePoolWithTag(
NonPagedPool,
DevExt->MouseAttributes.InputDataQueueLength *
sizeof(MOUSE_INPUT_DATA),
TAG_I8042);
if (!DevExt->MouseBuffer) {
ExFreePoolWithTag(DevExt->KeyboardBuffer, TAG_I8042);
DPRINT1("No memory for mouse buffer\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
InsertTailList(&DevExt->BusDevices, &FdoDevExt->BusDevices);
Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
}
else
DevExt->MouseExists = FALSE;
}
if (DirqlKeyboard > DirqlMouse)
DirqlMax = DirqlKeyboard;
else
DirqlMax = DirqlMouse;
if (DevExt->KeyboardExists) {
Status = IoConnectInterrupt(&DevExt->KeyboardInterruptObject,
I8042InterruptServiceKbd,
(PVOID)DevExt,
&DevExt->SpinLock,
MappedIrqKeyboard,
DirqlKeyboard,
DirqlMax,
LevelSensitive,
FALSE,
Affinity,
FALSE);
DPRINT("Keyboard Irq Status: %x\n", Status);
}
if (DevExt->MouseExists) {
Status = IoConnectInterrupt(&DevExt->MouseInterruptObject,
I8042InterruptServiceMouse,
(PVOID)DevExt,
&DevExt->SpinLock,
MappedIrqMouse,
DirqlMouse,
DirqlMax,
LevelSensitive,
FALSE,
Affinity,
FALSE);
DPRINT("Mouse Irq Status: %x\n", Status);
}
if (DirqlKeyboard > DirqlMouse)
DevExt->HighestDIRQLInterrupt = DevExt->KeyboardInterruptObject;
else
DevExt->HighestDIRQLInterrupt = DevExt->MouseInterruptObject;
DPRINT("I8042AddDevice done\n");
return(STATUS_SUCCESS);
}
NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
/*
* FUNCTION: Module entry point
*/
{
DPRINT("I8042 Driver 0.0.1\n");
I8042RegistryPath.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters");
I8042RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
I8042RegistryPath.MaximumLength,
TAG_I8042);
if (I8042RegistryPath.Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyUnicodeString(&I8042RegistryPath, RegistryPath);
RtlAppendUnicodeToString(&I8042RegistryPath, L"\\Parameters");
I8042RegistryPath.Buffer[I8042RegistryPath.Length / sizeof(WCHAR)] = 0;
DriverObject->MajorFunction[IRP_MJ_CREATE] = I8042CreateDispatch;
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
I8042InternalDeviceControl;
DriverObject->DriverStartIo = I8042StartIo;
DriverObject->DriverExtension->AddDevice = I8042AddDevice;
return(STATUS_SUCCESS);
}

View file

@ -0,0 +1,378 @@
#ifndef _I8042DRV_H
#define _I8042DRV_H
#include <ntddk.h>
#include <kbdmou.h>
#include <ntdd8042.h>
#ifdef _MSC_VER
#define STDCALL
#define DDKAPI
#endif
#define KEYBOARD_IRQ 1
#define MOUSE_IRQ 12
#define KBD_BUFFER_SIZE 32
#define WHEEL_DELTA 120
/*-----------------------------------------------------
* DeviceExtension
* --------------------------------------------------*/
typedef struct _COMMAND_CONTEXT
{
int NumInput;
int CurInput;
UCHAR * Input;
int NumOutput;
int CurOutput;
UCHAR * Output;
NTSTATUS Status;
BOOLEAN GotAck;
KEVENT Event;
PVOID DevExt;
} COMMAND_CONTEXT, *PCOMMAND_CONTEXT;
typedef enum _MOUSE_TIMEOUT_STATE
{
NoChange,
TimeoutStart,
TimeoutCancel
} MOUSE_TIMEOUT_STATE, *PMOUSE_TIMEOUT_STATE;
/* TODO: part of this should be in the _ATTRIBUTES structs instead */
typedef struct _I8042_SETTINGS
{
ULONG Headless; /* done */
ULONG CrashScroll;
ULONG CrashSysRq; /* done */
ULONG ReportResetErrors;
ULONG PollStatusIterations; /* done */
ULONG ResendIterations; /* done */
ULONG PollingIterations;
ULONG PollingIterationsMaximum;
ULONG OverrideKeyboardType;
ULONG OverrideKeyboardSubtype;
ULONG MouseResendStallTime;
ULONG MouseSynchIn100ns; /* done */
ULONG MouseResolution; /* done */
ULONG NumberOfButtons;
ULONG EnableWheelDetection;
} I8042_SETTINGS, *PI8042_SETTINGS;
typedef enum _I8042_MOUSE_TYPE
{
GenericPS2,
Intellimouse,
IntellimouseExplorer,
Ps2pp
} I8042_MOUSE_TYPE, *PI8042_MOUSE_TYPE;
typedef enum _I8042_DEVICE_TYPE
{
Keyboard,
Mouse
} I8042_DEVICE_TYPE, *PI8042_DEVICE_TYPE;
typedef struct _I8042_DEVICE
{
LIST_ENTRY ListEntry;
PDEVICE_OBJECT Pdo;
} I8042_DEVICE, *PI8042_DEVICE;
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT KeyboardObject;
PDEVICE_OBJECT MouseObject;
CONNECT_DATA KeyboardData;
CONNECT_DATA MouseData;
BOOLEAN KeyboardExists;
BOOLEAN KeyboardIsAT;
BOOLEAN MouseExists;
BOOLEAN KeyboardClaimed;
BOOLEAN MouseClaimed;
ULONG BusNumber;
LIST_ENTRY BusDevices;
INTERNAL_I8042_START_INFORMATION KeyboardStartInformation;
INTERNAL_I8042_START_INFORMATION MouseStartInformation;
INTERNAL_I8042_HOOK_KEYBOARD KeyboardHook;
INTERNAL_I8042_HOOK_MOUSE MouseHook;
PKINTERRUPT KeyboardInterruptObject;
PKINTERRUPT MouseInterruptObject;
PKINTERRUPT HighestDIRQLInterrupt;
KSPIN_LOCK SpinLock;
KDPC DpcKbd;
KDPC DpcMouse;
KTIMER TimerMouseTimeout;
KDPC DpcMouseTimeout;
MOUSE_TIMEOUT_STATE MouseTimeoutState;
BOOLEAN MouseTimeoutActive;
KEYBOARD_ATTRIBUTES KeyboardAttributes;
KEYBOARD_INDICATOR_PARAMETERS KeyboardIndicators;
KEYBOARD_TYPEMATIC_PARAMETERS KeyboardTypematic;
BOOLEAN WantAck;
BOOLEAN WantOutput;
BOOLEAN SignalEvent;
KEYBOARD_SCAN_STATE KeyboardScanState;
BOOLEAN KeyComplete;
KEYBOARD_INPUT_DATA *KeyboardBuffer;
ULONG KeysInBuffer;
MOUSE_ATTRIBUTES MouseAttributes;
MOUSE_STATE MouseState;
BOOLEAN MouseComplete;
MOUSE_RESET_SUBSTATE MouseResetState;
MOUSE_INPUT_DATA *MouseBuffer;
ULONG MouseInBuffer;
USHORT MouseButtonState;
ULARGE_INTEGER MousePacketStartTime;
UCHAR MouseLogiBuffer[3];
UCHAR MouseLogitechID;
I8042_MOUSE_TYPE MouseType;
OUTPUT_PACKET Packet;
ULONG PacketResends;
BOOLEAN PacketComplete;
NTSTATUS PacketResult;
UCHAR PacketBuffer[16];
UCHAR PacketPort;
PIRP CurrentIrp;
PDEVICE_OBJECT CurrentIrpDevice;
/* registry config values */
I8042_SETTINGS Settings;
/* Debugger stuff */
BOOLEAN TabPressed;
ULONG DebugKey;
PIO_WORKITEM DebugWorkItem;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
typedef struct _FDO_DEVICE_EXTENSION
{
PDEVICE_EXTENSION PortDevExt;
I8042_DEVICE_TYPE Type;
PDEVICE_OBJECT DeviceObject;
LIST_ENTRY BusDevices;
} FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION;
typedef struct _I8042_HOOK_WORKITEM
{
PIO_WORKITEM WorkItem;
PDEVICE_OBJECT Target;
PIRP Irp;
} I8042_HOOK_WORKITEM, *PI8042_HOOK_WORKITEM;
/*
* Some defines
*/
#define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
#define TAG_I8042 TAG('8', '0', '4', '2')
#define KBD_WRAP_MASK 0x1F
#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
/*
* Keyboard controller ports
*/
#define I8042_DATA_PORT ((PUCHAR)0x60)
#define I8042_CTRL_PORT ((PUCHAR)0x64)
/*
* Controller commands
*/
#define KBD_READ_MODE 0x20
#define KBD_WRITE_MODE 0x60
#define KBD_SELF_TEST 0xAA
#define KBD_LINE_TEST 0xAB
#define KBD_CTRL_ENABLE 0xAE
#define MOUSE_LINE_TEST 0xA9
#define MOUSE_CTRL_ENABLE 0xA8
#define KBD_READ_OUTPUT_PORT 0xD0
#define KBD_WRITE_OUTPUT_PORT 0xD1
/*
* Keyboard commands
*/
#define KBD_SET_LEDS 0xED
#define KBD_GET_ID 0xF2
#define KBD_ENABLE 0xF4
#define KBD_DISABLE 0xF5
#define KBD_RESET 0xFF
/*
* Keyboard responces
*/
#define KBD_BATCC 0xAA
#define KBD_ACK 0xFA
#define KBD_NACK 0xFC
#define KBD_RESEND 0xFE
/*
* Controller status register bits
*/
#define KBD_OBF 0x01
#define KBD_IBF 0x02
#define KBD_AUX 0x10
#define KBD_GTO 0x40
#define KBD_PERR 0x80
/*
* Controller command byte bits
*/
#define CCB_KBD_INT_ENAB 0x01
#define CCB_MOUSE_INT_ENAB 0x02
#define CCB_SYSTEM_FLAG 0x04
#define CCB_IGN_KEY_LOCK 0x08
#define CCB_KBD_DISAB 0x10
#define CCB_MOUSE_DISAB 0x20
#define CCB_TRANSLATE 0x40
/*
* LED bits
*/
#define KBD_LED_SCROLL 0x01
#define KBD_LED_NUM 0x02
#define KBD_LED_CAPS 0x04
/*
* Mouse responses
*/
#define MOUSE_ACK 0xFA
#define MOUSE_ERROR 0xFC
#define MOUSE_NACK 0xFE
/* i8042prt.c */
extern UNICODE_STRING I8042RegistryPath;
NTSTATUS I8042ReadData(UCHAR *Data);
NTSTATUS I8042ReadStatus(UCHAR *Status);
NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data);
VOID I8042Flush();
VOID STDCALL I8042IsrWritePort(PDEVICE_EXTENSION DevExt,
UCHAR Value,
UCHAR SelectCmd);
NTSTATUS STDCALL I8042SynchWritePort(PDEVICE_EXTENSION DevExt,
UCHAR Port,
UCHAR Value,
BOOLEAN WaitForAck);
NTSTATUS STDCALL I8042StartPacket(PDEVICE_EXTENSION DevExt,
PDEVICE_OBJECT Device,
PUCHAR Bytes,
ULONG ByteCount,
PIRP Irp);
BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt,
UCHAR Output);
VOID I8042PacketDpc(PDEVICE_EXTENSION DevExt);
VOID STDCALL I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject,
PVOID Context);
BOOLEAN I8042Write(PDEVICE_EXTENSION DevExt, PUCHAR addr, UCHAR data);
NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath);
/* keyboard.c */
VOID STDCALL I8042IsrWritePortKbd(PVOID Context,
UCHAR Value);
NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context,
UCHAR Value,
BOOLEAN WaitForAck);
BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt,
VOID * Context);
VOID STDCALL I8042DpcRoutineKbd(PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2);
BOOLEAN STDCALL I8042StartIoKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS STDCALL I8042InternalDeviceControlKbd(PDEVICE_OBJECT DeviceObject,
PIRP Irp);
BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt);
BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt);
BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt);
/* registry.c */
VOID STDCALL I8042ReadRegistry(PDRIVER_OBJECT DriverObject,
PDEVICE_EXTENSION DevExt);
/* mouse.c */
VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2);
VOID STDCALL I8042DpcRoutineMouseTimeout(PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2);
BOOLEAN STDCALL I8042InterruptServiceMouse(struct _KINTERRUPT *Interrupt,
VOID *Context);
NTSTATUS STDCALL I8042InternalDeviceControlMouse(PDEVICE_OBJECT DeviceObject,
PIRP Irp);
VOID STDCALL I8042QueueMousePacket(PVOID Context);
VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt,
USHORT Mask);
VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt,
UCHAR Output);
BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt);
BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt);
BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt);
/* ps2pp.c */
VOID I8042MouseHandlePs2pp(PDEVICE_EXTENSION DevExt, UCHAR Input);
#endif // _KEYBOARD_H_

View file

@ -0,0 +1,14 @@
<module name="i8042prt" type="kernelmodedriver" installbase="system32/drivers" installname="i8042prt.sys">
<bootstrap base="reactos" />
<include base="i8042prt">.</include>
<define name="__USE_W32API" />
<define name="__REACTOS__" />
<library>ntoskrnl</library>
<library>hal</library>
<file>i8042prt.c</file>
<file>keyboard.c</file>
<file>mouse.c</file>
<file>ps2pp.c</file>
<file>registry.c</file>
<file>i8042prt.rc</file>
</module>

View file

@ -0,0 +1,5 @@
#define REACTOS_VERSION_DLL
#define REACTOS_STR_FILE_DESCRIPTION "I8042 Port Device Driver\0"
#define REACTOS_STR_INTERNAL_NAME "i8042prt\0"
#define REACTOS_STR_ORIGINAL_FILENAME "i8042prt.sys\0"
#include <reactos/version.rc>

View file

@ -0,0 +1,776 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/keyboard.c
* PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
* keyboard specifics
* PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
* Jason Filby (jasonfilby@yahoo.com)
* Tinus
*/
/* INCLUDES ****************************************************************/
#include "i8042prt.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
static UCHAR TypematicTable[] = {
0x00, 0x00, 0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x10, 0x12, /* 0-9 */
0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1A, /* 10-19 */
0x1B, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E };
typedef struct _LOCAL_KEYBOARD_INDICATOR_TRANSLATION {
USHORT NumberOfIndicatorKeys;
INDICATOR_LIST IndicatorList[3];
} LOCAL_KEYBOARD_INDICATOR_TRANSLATION, *PLOCAL_KEYBOARD_INDICATOR_TRANSLATION;
static LOCAL_KEYBOARD_INDICATOR_TRANSLATION IndicatorTranslation = { 3, {
{0x3A, KEYBOARD_CAPS_LOCK_ON},
{0x45, KEYBOARD_NUM_LOCK_ON},
{0x46, KEYBOARD_SCROLL_LOCK_ON}}};
static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject,
PVOID Context);
/* FUNCTIONS *****************************************************************/
/*
* These functions are callbacks for filter driver custom interrupt
* service routines.
*/
VOID STDCALL I8042IsrWritePortKbd(PVOID Context,
UCHAR Value)
{
I8042IsrWritePort(Context, Value, 0);
}
static VOID STDCALL I8042QueueKeyboardPacket(PVOID Context)
{
PDEVICE_OBJECT DeviceObject = Context;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
DevExt->KeyComplete = TRUE;
DevExt->KeysInBuffer++;
if (DevExt->KeysInBuffer >
DevExt->KeyboardAttributes.InputDataQueueLength) {
DPRINT1("Keyboard buffer overflow\n");
DevExt->KeysInBuffer--;
}
DPRINT("Irq completes key\n");
KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
}
/*
* These functions are callbacks for filter driver custom
* initialization routines.
*/
NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context,
UCHAR Value,
BOOLEAN WaitForAck)
{
return I8042SynchWritePort((PDEVICE_EXTENSION)Context,
0,
Value,
WaitForAck);
}
BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt,
VOID * Context)
{
UCHAR Output;
UCHAR PortStatus;
NTSTATUS Status;
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context;
BOOLEAN HookContinue = FALSE, HookReturn;
ULONG Iterations = 0;
KEYBOARD_INPUT_DATA *InputData =
DevExt->KeyboardBuffer + DevExt->KeysInBuffer;
do {
Status = I8042ReadStatus(&PortStatus);
DPRINT("PortStatus: %x\n", PortStatus);
Status = I8042ReadData(&Output);
Iterations++;
if (STATUS_SUCCESS == Status)
break;
KeStallExecutionProcessor(1);
} while (Iterations < DevExt->Settings.PollStatusIterations);
if (STATUS_SUCCESS != Status) {
DPRINT("Spurious I8042 interrupt\n");
return FALSE;
}
DPRINT("Got: %x\n", Output);
if (DevExt->KeyboardHook.IsrRoutine) {
HookReturn = DevExt->KeyboardHook.IsrRoutine(
DevExt->KeyboardHook.Context,
InputData,
&DevExt->Packet,
PortStatus,
&Output,
&HookContinue,
&DevExt->KeyboardScanState);
if (!HookContinue)
return HookReturn;
}
if (I8042PacketIsr(DevExt, Output)) {
if (DevExt->PacketComplete) {
DPRINT("Packet complete\n");
KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
}
DPRINT("Irq eaten by packet\n");
return TRUE;
}
DPRINT("Irq is keyboard input\n");
if (Normal == DevExt->KeyboardScanState) {
switch (Output) {
case 0xe0:
DevExt->KeyboardScanState = GotE0;
return TRUE;
case 0xe1:
DevExt->KeyboardScanState = GotE1;
return TRUE;
default:
;/* continue */
}
}
InputData->Flags = 0;
switch (DevExt->KeyboardScanState) {
case GotE0:
InputData->Flags |= KEY_E0;
break;
case GotE1:
InputData->Flags |= KEY_E1;
break;
default:
;
}
DevExt->KeyboardScanState = Normal;
if (Output & 0x80)
InputData->Flags |= KEY_BREAK;
else
InputData->Flags |= KEY_MAKE;
InputData->MakeCode = Output & 0x7f;
I8042QueueKeyboardPacket(DevExt->KeyboardObject);
return TRUE;
}
VOID STDCALL I8042DpcRoutineKbd(PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2)
{
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1;
ULONG KeysTransferred = 0;
ULONG KeysInBufferCopy;
KIRQL Irql;
I8042PacketDpc(DevExt);
if (!DevExt->KeyComplete)
return;
/* We got the interrupt as it was being enabled, too bad */
if (!DevExt->HighestDIRQLInterrupt)
return;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->KeyComplete = FALSE;
KeysInBufferCopy = DevExt->KeysInBuffer;
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
/* Test for TAB (debugging) */
if (DevExt->Settings.CrashSysRq) {
PKEYBOARD_INPUT_DATA InputData = DevExt->KeyboardBuffer +
KeysInBufferCopy - 1;
if (InputData->MakeCode == 0x0F) {
DPRINT("Tab!\n");
DevExt->TabPressed = !(InputData->Flags & KEY_BREAK);
} else if (DevExt->TabPressed) {
DPRINT ("Queueing work item %x\n", DevExt->DebugWorkItem);
DevExt->DebugKey = InputData->MakeCode;
DevExt->TabPressed = FALSE;
IoQueueWorkItem(DevExt->DebugWorkItem,
&(I8042DebugWorkItem),
DelayedWorkQueue,
DevExt);
}
}
DPRINT ("Send a key\n");
if (!DevExt->KeyboardData.ClassService)
return;
((PSERVICE_CALLBACK_ROUTINE) DevExt->KeyboardData.ClassService)(
DevExt->KeyboardData.ClassDeviceObject,
DevExt->KeyboardBuffer,
DevExt->KeyboardBuffer + KeysInBufferCopy,
&KeysTransferred);
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->KeysInBuffer -= KeysTransferred;
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
}
/* You have to send the rate/delay in a somewhat awkward format */
static UCHAR I8042GetTypematicByte(USHORT Rate, USHORT Delay)
{
UCHAR ret;
if (Rate < 3) {
ret = 0x0;
} else if (Rate > 26) {
ret = 0x1F;
} else {
ret = TypematicTable[Rate];
}
if (Delay < 375) {
;
} else if (Delay < 625) {
ret |= 0x20;
} else if (Delay < 875) {
ret |= 0x40;
} else {
ret |= 0x60;
}
return ret;
}
/*
* Process the keyboard internal device requests
* returns FALSE if it doesn't understand the
* call so someone else can handle it.
*/
BOOLEAN STDCALL I8042StartIoKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION Stk;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
Stk = IoGetCurrentIrpStackLocation(Irp);
switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER:
I8042StartPacket(
DevExt,
DeviceObject,
Stk->Parameters.DeviceIoControl.Type3InputBuffer,
Stk->Parameters.DeviceIoControl.InputBufferLength,
Irp);
break;
case IOCTL_KEYBOARD_SET_INDICATORS:
DevExt->PacketBuffer[0] = 0xED;
DevExt->PacketBuffer[1] = 0;
if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON)
DevExt->PacketBuffer[1] |= 0x04;
if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON)
DevExt->PacketBuffer[1] |= 0x02;
if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON)
DevExt->PacketBuffer[1] |= 0x01;
I8042StartPacket(DevExt,
DeviceObject,
DevExt->PacketBuffer,
2,
Irp);
break;
case IOCTL_KEYBOARD_SET_TYPEMATIC:
DevExt->PacketBuffer[0] = 0xF3;
DevExt->PacketBuffer[1] = I8042GetTypematicByte(
DevExt->KeyboardTypematic.Rate,
DevExt->KeyboardTypematic.Delay);
I8042StartPacket(DevExt,
DeviceObject,
DevExt->PacketBuffer,
2,
Irp);
break;
default:
return FALSE;
}
return TRUE;
}
/*
* Runs the keyboard IOCTL_INTERNAL dispatch.
* Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request
* so someone else can have a try at it.
*/
NTSTATUS STDCALL I8042InternalDeviceControlKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION Stk;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
DPRINT("InternalDeviceControl\n");
Irp->IoStatus.Information = 0;
Stk = IoGetCurrentIrpStackLocation(Irp);
switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_INTERNAL_KEYBOARD_CONNECT:
DPRINT("IOCTL_INTERNAL_KEYBOARD_CONNECT\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CONNECT_DATA)) {
DPRINT1("Keyboard IOCTL_INTERNAL_KEYBOARD_CONNECT "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
goto intcontfailure;
}
if (!DevExt->KeyboardExists) {
Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
goto intcontfailure;
}
if (DevExt->KeyboardClaimed) {
DPRINT1("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
"Keyboard is already claimed\n");
Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
goto intcontfailure;
}
memcpy(&DevExt->KeyboardData,
Stk->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof(CONNECT_DATA));
DevExt->KeyboardHook.IsrWritePort = I8042IsrWritePortKbd;
DevExt->KeyboardHook.QueueKeyboardPacket =
I8042QueueKeyboardPacket;
DevExt->KeyboardHook.CallContext = DevExt;
{
PIO_WORKITEM WorkItem;
PI8042_HOOK_WORKITEM WorkItemData;
WorkItem = IoAllocateWorkItem(DeviceObject);
if (!WorkItem) {
DPRINT ("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
"Can't allocate work item\n");
Irp->IoStatus.Status =
STATUS_INSUFFICIENT_RESOURCES;
goto intcontfailure;
}
WorkItemData = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(I8042_HOOK_WORKITEM),
TAG_I8042);
if (!WorkItemData) {
DPRINT ("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
"Can't allocate work item data\n");
Irp->IoStatus.Status =
STATUS_INSUFFICIENT_RESOURCES;
IoFreeWorkItem(WorkItem);
goto intcontfailure;
}
WorkItemData->WorkItem = WorkItem;
WorkItemData->Target =
DevExt->KeyboardData.ClassDeviceObject;
WorkItemData->Irp = Irp;
IoMarkIrpPending(Irp);
IoQueueWorkItem(WorkItem,
I8042SendHookWorkItem,
DelayedWorkQueue,
WorkItemData);
Irp->IoStatus.Status = STATUS_PENDING;
}
break;
case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER:
DPRINT("IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
goto intcontfailure;
}
if (!DevExt->KeyboardInterruptObject) {
Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
goto intcontfailure;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
Irp->IoStatus.Status = STATUS_PENDING;
break;
case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
DPRINT("IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n");
if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(KEYBOARD_ATTRIBUTES)) {
DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_ATTRIBUTES "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
goto intcontfailure;
}
memcpy(Irp->AssociatedIrp.SystemBuffer,
&DevExt->KeyboardAttributes,
sizeof(KEYBOARD_ATTRIBUTES));
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IOCTL_KEYBOARD_QUERY_INDICATORS:
DPRINT("IOCTL_KEYBOARD_QUERY_INDICATORS\n");
if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_INDICATORS "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
goto intcontfailure;
}
memcpy(Irp->AssociatedIrp.SystemBuffer,
&DevExt->KeyboardIndicators,
sizeof(KEYBOARD_INDICATOR_PARAMETERS));
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
DPRINT("IOCTL_KEYBOARD_QUERY_TYPEMATIC\n");
if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_TYPEMATIC "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
goto intcontfailure;
}
memcpy(Irp->AssociatedIrp.SystemBuffer,
&DevExt->KeyboardTypematic,
sizeof(KEYBOARD_TYPEMATIC_PARAMETERS));
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IOCTL_KEYBOARD_SET_INDICATORS:
DPRINT("IOCTL_KEYBOARD_SET_INDICATORS\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength <
sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
DPRINT("Keyboard IOCTL_KEYBOARD_SET_INDICTATORS "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
goto intcontfailure;
}
memcpy(&DevExt->KeyboardIndicators,
Irp->AssociatedIrp.SystemBuffer,
sizeof(KEYBOARD_INDICATOR_PARAMETERS));
DPRINT("%x\n", DevExt->KeyboardIndicators.LedFlags);
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
Irp->IoStatus.Status = STATUS_PENDING;
break;
case IOCTL_KEYBOARD_SET_TYPEMATIC:
DPRINT("IOCTL_KEYBOARD_SET_TYPEMATIC\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength <
sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
DPRINT("Keyboard IOCTL_KEYBOARD_SET_TYPEMATIC "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
goto intcontfailure;
}
memcpy(&DevExt->KeyboardTypematic,
Irp->AssociatedIrp.SystemBuffer,
sizeof(KEYBOARD_TYPEMATIC_PARAMETERS));
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
Irp->IoStatus.Status = STATUS_PENDING;
break;
case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
/* We should check the UnitID, but it's kind of pointless as
* all keyboards are supposed to have the same one
*/
if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)) {
DPRINT("IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: "
"invalid buffer size (expected)\n");
/* It's to query the buffer size */
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
goto intcontfailure;
}
Irp->IoStatus.Information =
sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION);
memcpy(Irp->AssociatedIrp.SystemBuffer,
&IndicatorTranslation,
sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION));
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD:
/* Nothing to do here */
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
default:
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
intcontfailure:
return Irp->IoStatus.Status;
}
/* This is all pretty confusing. There's more than one way to
* disable/enable the keyboard. You can send KBD_ENABLE to the
* keyboard, and it will start scanning keys. Sending KBD_DISABLE
* will disable the key scanning but also reset the parameters to
* defaults.
*
* You can also send 0xAE to the controller for enabling the
* keyboard clock line and 0xAD for disabling it. Then it'll
* automatically get turned on at the next command. The last
* way is by modifying the bit that drives the clock line in the
* 'command byte' of the controller. This is almost, but not quite,
* the same as the AE/AD thing. The difference can be used to detect
* some really old broken keyboard controllers which I hope won't be
* necessary.
*
* We change the command byte, sending KBD_ENABLE/DISABLE seems to confuse
* some kvm switches.
*/
BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt)
{
UCHAR Value;
NTSTATUS Status;
DPRINT("Enable keyboard\n");
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value &= ~CCB_KBD_DISAB; // don't disable keyboard
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
return TRUE;
}
static BOOLEAN STDCALL I8042KeyboardDefaultsAndDisable(PDEVICE_EXTENSION DevExt)
{
UCHAR Value;
NTSTATUS Status;
DPRINT("Disabling keyboard\n");
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value |= CCB_KBD_DISAB; // disable keyboard
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
return TRUE;
}
BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt)
{
UCHAR Value;
NTSTATUS Status;
DPRINT("Enabling keyboard interrupt\n");
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value &= ~CCB_KBD_DISAB; // don't disable keyboard
Value |= CCB_KBD_INT_ENAB; // enable keyboard interrupts
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
return TRUE;
}
BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt)
{
NTSTATUS Status;
UCHAR Value;
UCHAR Value2;
ULONG RetryCount = 10;
DPRINT("Detecting keyboard\n");
I8042KeyboardDefaultsAndDisable(DevExt);
do {
I8042Flush();
Status = I8042SynchWritePort(DevExt, 0, KBD_GET_ID, TRUE);
} while (STATUS_IO_TIMEOUT == Status && RetryCount--);
if (!NT_SUCCESS(Status)) {
DPRINT1("Can't write GET_ID (%x)\n", Status);
/* Could be an AT keyboard */
DevExt->KeyboardIsAT = TRUE;
goto detectsetleds;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after GET_ID\n");
/* Could be an AT keyboard */
DevExt->KeyboardIsAT = TRUE;
goto detectsetleds;
}
DevExt->KeyboardIsAT = FALSE;
if (Value != 0xAB && Value != 0xAC) {
DPRINT("Bad ID: %x\n", Value);
/* This is certainly not a keyboard */
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value2);
if (!NT_SUCCESS(Status)) {
DPRINT("Partial ID\n");
return FALSE;
}
DPRINT("Keyboard ID: 0x%x 0x%x\n", Value, Value2);
detectsetleds:
I8042Flush(); /* Flush any bytes left over from GET_ID */
Status = I8042SynchWritePort(DevExt, 0, KBD_SET_LEDS, TRUE);
if (!NT_SUCCESS(Status)) {
DPRINT("Can't write SET_LEDS (%x)\n", Status);
return FALSE;
}
Status = I8042SynchWritePort(DevExt, 0, 0, TRUE);
if (!NT_SUCCESS(Status)) {
DPRINT("Can't finish SET_LEDS (%x)\n", Status);
return FALSE;
}
// Turn on translation
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value |= 0x40; // enable keyboard translation
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
return TRUE;
}
/* debug stuff */
VOID STDCALL
KdpServiceDispatcher(ULONG Code, PVOID Context1, PVOID Context2);
#define EnterDebugger ((PVOID)0x25)
static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject,
PVOID Context)
{
ULONG Key;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
Key = InterlockedExchange((PLONG)&DevExt->DebugKey, 0);
DPRINT("Debug key: %x\n", Key);
if (!Key)
return;
#ifdef __REACTOS__
/* We hope kernel would understand this. If
* that's not the case, nothing would happen.
*/
KdpServiceDispatcher(TAG('R', 'o', 's', ' '), (PVOID)Key, NULL);
#endif /* __REACTOS__ */
}

View file

@ -0,0 +1,948 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/mouse.c
* PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
* mouse specifics
* PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
* Jason Filby (jasonfilby@yahoo.com)
* Tinus
*/
/* INCLUDES ****************************************************************/
#include "i8042prt.h"
#define NDEBUG
#include <debug.h>
/*
* These functions are callbacks for filter driver custom interrupt
* service routines.
*/
static VOID STDCALL I8042IsrWritePortMouse(PVOID Context,
UCHAR Value)
{
I8042IsrWritePort(Context, Value, 0xD4);
}
#if 0
static NTSTATUS STDCALL I8042SynchWritePortMouse(PVOID Context,
UCHAR Value,
BOOLEAN WaitForAck)
{
return I8042SynchWritePort((PDEVICE_EXTENSION)Context,
0xD4,
Value,
WaitForAck);
}
#endif
/* Test if packets are taking too long to come in. If they do, we
* might have gotten out of sync and should just drop what we have.
*
* If we want to be totally right, we'd also have to keep a count of
* errors, and totally reset the mouse after too much of them (can
* happen if the user is using a KVM switch and an OS on another port
* resets the mouse, or if the user hotplugs the mouse, or if we're just
* generally unlucky). Also note the input parsing routine where we
* drop invalid input packets.
*/
static VOID STDCALL I8042MouseInputTestTimeout(PDEVICE_EXTENSION DevExt)
{
ULARGE_INTEGER Now;
if (DevExt->MouseState == MouseExpectingACK ||
DevExt->MouseState == MouseResetting)
return;
Now.QuadPart = KeQueryInterruptTime();
if (DevExt->MouseState != MouseIdle) {
/* Check if the last byte came too long ago */
if (Now.QuadPart - DevExt->MousePacketStartTime.QuadPart >
DevExt->Settings.MouseSynchIn100ns) {
DPRINT("Mouse input packet timeout\n");
DevExt->MouseState = MouseIdle;
}
}
if (DevExt->MouseState == MouseIdle)
DevExt->MousePacketStartTime.QuadPart = Now.QuadPart;
}
/*
* Call the customization hook. The Ret2 parameter is about wether
* we should go on with the interrupt. The return value is what
* we should return (indicating to the system wether someone else
* should try to handle the interrupt)
*/
static BOOLEAN STDCALL I8042MouseCallIsrHook(PDEVICE_EXTENSION DevExt,
UCHAR Status,
PUCHAR Input,
PBOOLEAN ToReturn)
{
BOOLEAN HookReturn, HookContinue;
HookContinue = FALSE;
if (DevExt->MouseHook.IsrRoutine) {
HookReturn = DevExt->MouseHook.IsrRoutine(
DevExt->MouseHook.Context,
DevExt->MouseBuffer + DevExt->MouseInBuffer,
&DevExt->Packet,
Status,
Input,
&HookContinue,
&DevExt->MouseState,
&DevExt->MouseResetState);
if (!HookContinue) {
*ToReturn = HookReturn;
return TRUE;
}
}
return FALSE;
}
static BOOLEAN STDCALL I8042MouseResetIsr(PDEVICE_EXTENSION DevExt,
UCHAR Status,
PUCHAR Value)
{
BOOLEAN ToReturn = FALSE;
if (I8042MouseCallIsrHook(DevExt, Status, Value, &ToReturn))
return ToReturn;
if (MouseResetting != DevExt->MouseState) {
return FALSE;
}
DevExt->MouseTimeoutState = TimeoutStart;
switch (DevExt->MouseResetState) {
case 1100: /* the first ack, drop it. */
DevExt->MouseResetState = ExpectingReset;
return TRUE;
/* First, 0xFF is sent. The mouse is supposed to say AA00 if ok,
* FC00 if not.
*/
case ExpectingReset:
if (0xAA == *Value) {
DevExt->MouseResetState++;
} else {
DevExt->MouseExists = FALSE;
DevExt->MouseState = MouseIdle;
DPRINT("Mouse returned bad reset reply: "
"%x (expected aa)\n", *Value);
}
return TRUE;
case ExpectingResetId:
if (0x00 == *Value) {
DevExt->MouseResetState++;
DevExt->MouseType = GenericPS2;
I8042IsrWritePortMouse(DevExt, 0xF2);
} else {
DevExt->MouseExists = FALSE;
DevExt->MouseState = MouseIdle;
DPRINT1("Mouse returned bad reset reply part two: "
"%x (expected 0)\n", *Value);
}
return TRUE;
case ExpectingGetDeviceIdACK:
if (MOUSE_ACK == *Value) {
DevExt->MouseResetState++;
} else if (MOUSE_NACK == *Value ||
MOUSE_ERROR == *Value) {
DevExt->MouseResetState++;
/* Act as if 00 (normal mouse) was received */
DPRINT("Mouse doesn't support 0xd2, "
"(returns %x, expected %x), faking.\n",
*Value, MOUSE_ACK);
*Value = 0;
I8042MouseResetIsr(DevExt, Status, Value);
}
return TRUE;
case ExpectingGetDeviceIdValue:
switch (*Value) {
case 0x02:
DevExt->MouseAttributes.MouseIdentifier =
BALLPOINT_I8042_HARDWARE;
break;
case 0x03:
case 0x04:
DevExt->MouseAttributes.MouseIdentifier =
WHEELMOUSE_I8042_HARDWARE;
break;
default:
DevExt->MouseAttributes.MouseIdentifier =
MOUSE_I8042_HARDWARE;
}
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0xE8);
return TRUE;
case ExpectingSetResolutionDefaultACK:
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0x00);
return TRUE;
case ExpectingSetResolutionDefaultValueACK:
DevExt->MouseResetState = ExpectingSetScaling1to1ACK;
I8042IsrWritePortMouse(DevExt, 0xE6);
return TRUE;
case ExpectingSetScaling1to1ACK:
case ExpectingSetScaling1to1ACK2:
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0xE6);
return TRUE;
case ExpectingSetScaling1to1ACK3:
DevExt->MouseResetState++;
I8042IsrWritePortMouse(DevExt, 0xE9);
return TRUE;
case ExpectingReadMouseStatusACK:
DevExt->MouseResetState++;
return TRUE;
case ExpectingReadMouseStatusByte1:
DevExt->MouseLogiBuffer[0] = *Value;
DevExt->MouseResetState++;
return TRUE;
case ExpectingReadMouseStatusByte2:
DevExt->MouseLogiBuffer[1] = *Value;
DevExt->MouseResetState++;
return TRUE;
case ExpectingReadMouseStatusByte3:
DevExt->MouseLogiBuffer[2] = *Value;
/* Now MouseLogiBuffer is a set of info. If the second
* byte is 0, the mouse didn't understand the magic
* code. Otherwise, it it a Logitech and the second byte
* is the number of buttons, bit 7 of the first byte tells
* if it understands special E7 commands, the rest is an ID.
*/
if (DevExt->MouseLogiBuffer[1]) {
DevExt->MouseAttributes.NumberOfButtons =
DevExt->MouseLogiBuffer[1];
/* For some reason the ID is the wrong way around */
DevExt->MouseLogitechID =
((DevExt->MouseLogiBuffer[0] >> 4) & 0x07) |
((DevExt->MouseLogiBuffer[0] << 3) & 0x78);
DevExt->MouseType = Ps2pp;
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = ExpectingSetSamplingRateACK;
return TRUE;
/* TODO: Go through EnableWheel and Enable5Buttons */
}
DevExt->MouseResetState = EnableWheel;
I8042MouseResetIsr(DevExt, Status, Value);
return TRUE;
case EnableWheel:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = 1001;
return TRUE;
case 1001:
I8042IsrWritePortMouse(DevExt, 0xc8);
DevExt->MouseResetState++;
return TRUE;
case 1002:
case 1004:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState++;
return TRUE;
case 1003:
I8042IsrWritePortMouse(DevExt, 0x64);
DevExt->MouseResetState++;
return TRUE;
case 1005:
I8042IsrWritePortMouse(DevExt, 0x50);
DevExt->MouseResetState++;
return TRUE;
case 1006:
I8042IsrWritePortMouse(DevExt, 0xf2);
DevExt->MouseResetState++;
return TRUE;
case 1007:
/* Ignore ACK */
DevExt->MouseResetState++;
return TRUE;
case 1008:
/* Now if the value == 3, it's either an Intellimouse
* or Intellimouse Explorer. */
if (0x03 == *Value) {
DevExt->MouseAttributes.NumberOfButtons = 3;
DevExt->MouseAttributes.MouseIdentifier =
WHEELMOUSE_I8042_HARDWARE;
DevExt->MouseType = Intellimouse;
DevExt->MouseResetState = Enable5Buttons;
I8042MouseResetIsr(DevExt, Status, Value);
return TRUE;
} /* Else, just set the default settings and be done */
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = ExpectingSetSamplingRateACK;
return TRUE;
case Enable5Buttons:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = 1021;
return TRUE;
case 1022:
case 1024:
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState++;
return TRUE;
case 1021:
case 1023:
I8042IsrWritePortMouse(DevExt, 0xc8);
DevExt->MouseResetState++;
return TRUE;
case 1025:
I8042IsrWritePortMouse(DevExt, 0x50);
DevExt->MouseResetState++;
return TRUE;
case 1026:
I8042IsrWritePortMouse(DevExt, 0xf2);
DevExt->MouseResetState++;
return TRUE;
case 1027:
if (0x04 == *Value) {
DevExt->MouseAttributes.NumberOfButtons = 5;
DevExt->MouseAttributes.MouseIdentifier =
WHEELMOUSE_I8042_HARDWARE;
DevExt->MouseType = IntellimouseExplorer;
}
I8042IsrWritePortMouse(DevExt, 0xf3);
DevExt->MouseResetState = ExpectingSetSamplingRateACK;
return TRUE;
case ExpectingSetSamplingRateACK:
I8042IsrWritePortMouse(DevExt,
(UCHAR)DevExt->MouseAttributes.SampleRate);
DevExt->MouseResetState++;
return TRUE;
case ExpectingSetSamplingRateValueACK:
if (MOUSE_NACK == *Value) {
I8042IsrWritePortMouse(DevExt, 0x3c);
DevExt->MouseAttributes.SampleRate = 60;
DevExt->MouseResetState = 1040;
return TRUE;
}
case 1040: /* Fallthrough */
I8042IsrWritePortMouse(DevExt, 0xe8);
DevExt->MouseResetState = ExpectingFinalResolutionACK;
return TRUE;
case ExpectingFinalResolutionACK:
I8042IsrWritePortMouse(DevExt,
(UCHAR)(DevExt->Settings.MouseResolution & 0xff));
DPRINT("%x\n", DevExt->Settings.MouseResolution);
DevExt->MouseResetState = ExpectingFinalResolutionValueACK;
return TRUE;
case ExpectingFinalResolutionValueACK:
I8042IsrWritePortMouse(DevExt, 0xf4);
DevExt->MouseResetState = ExpectingEnableACK;
return TRUE;
case ExpectingEnableACK:
DevExt->MouseState = MouseIdle;
DevExt->MouseTimeoutState = TimeoutCancel;
DPRINT("Mouse type = %d\n", DevExt->MouseType);
return TRUE;
default:
if (DevExt->MouseResetState < 100 ||
DevExt->MouseResetState > 999)
DPRINT1("MouseResetState went out of range: %d\n",
DevExt->MouseResetState);
return FALSE;
}
}
/*
* Prepare for reading the next packet and queue the dpc for handling
* this one.
*
* Context is the device object.
*/
VOID STDCALL I8042QueueMousePacket(PVOID Context)
{
PDEVICE_OBJECT DeviceObject = Context;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
DevExt->MouseComplete = TRUE;
DevExt->MouseInBuffer++;
if (DevExt->MouseInBuffer >
DevExt->MouseAttributes.InputDataQueueLength) {
DPRINT1("Mouse buffer overflow\n");
DevExt->MouseInBuffer--;
}
DPRINT("Irq completes mouse packet\n");
KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL);
}
/*
* Updates ButtonFlags according to RawButtons and a saved state;
* Only takes in account the bits that are set in Mask
*/
VOID STDCALL I8042MouseHandleButtons(PDEVICE_EXTENSION DevExt,
USHORT Mask)
{
PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
DevExt->MouseInBuffer;
USHORT NewButtonData = (USHORT)(MouseInput->RawButtons & Mask);
USHORT ButtonDiff = (NewButtonData ^ DevExt->MouseButtonState) & Mask;
/* Note that the defines are such:
* MOUSE_LEFT_BUTTON_DOWN 1
* MOUSE_LEFT_BUTTON_UP 2
*/
MouseInput->ButtonFlags |= (NewButtonData & ButtonDiff) |
(((~(NewButtonData)) << 1) &
(ButtonDiff << 1)) |
(MouseInput->RawButtons & 0xfc00);
DPRINT("Left raw/up/down: %d/%d/%d\n", MouseInput->RawButtons & MOUSE_LEFT_BUTTON_DOWN,
MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN,
MouseInput->ButtonFlags & MOUSE_LEFT_BUTTON_UP);
DevExt->MouseButtonState = (DevExt->MouseButtonState & ~Mask) |
(NewButtonData & Mask);
}
VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt,
UCHAR Output)
{
PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
DevExt->MouseInBuffer;
CHAR Scroll;
switch (DevExt->MouseState) {
case MouseIdle:
/* This bit should be 1, if not drop the packet, we
* might be lucky and get in sync again
*/
if (!(Output & 8)) {
DPRINT1("Bad input, dropping..\n");
return;
}
MouseInput->Buttons = 0;
MouseInput->RawButtons = 0;
MouseInput->Flags = MOUSE_MOVE_RELATIVE;
/* Note how we ignore the overflow bits, like Windows
* is said to do. There's no reasonable thing to do
* anyway.
*/
if (Output & 16)
MouseInput->LastX = 1;
else
MouseInput->LastX = 0;
if (Output & 32)
MouseInput->LastY = 1;
else
MouseInput->LastY = 0;
if (Output & 1) {
MouseInput->RawButtons |= MOUSE_LEFT_BUTTON_DOWN;
}
if (Output & 2) {
MouseInput->RawButtons |= MOUSE_RIGHT_BUTTON_DOWN;
}
if (Output & 4) {
MouseInput->RawButtons |= MOUSE_MIDDLE_BUTTON_DOWN;
}
DevExt->MouseState = XMovement;
break;
case XMovement:
if (MouseInput->LastX)
MouseInput->LastX = (LONG) Output - 256;
else
MouseInput->LastX = Output;
DevExt->MouseState = YMovement;
break;
case YMovement:
if (MouseInput->LastY)
MouseInput->LastY = (LONG)Output - 256;
else
MouseInput->LastY = (LONG)Output;
/* Windows wants it the other way around */
MouseInput->LastY = -MouseInput->LastY;
if (DevExt->MouseType == GenericPS2 ||
DevExt->MouseType == Ps2pp) {
I8042MouseHandleButtons(DevExt,
MOUSE_LEFT_BUTTON_DOWN |
MOUSE_RIGHT_BUTTON_DOWN |
MOUSE_MIDDLE_BUTTON_DOWN);
I8042QueueMousePacket(
DevExt->MouseObject);
DevExt->MouseState = MouseIdle;
} else {
DevExt->MouseState = ZMovement;
}
break;
case ZMovement:
Scroll = Output & 0x0f;
if (Scroll & 8)
Scroll |= 0xf0;
if (Scroll) {
MouseInput->RawButtons |= MOUSE_WHEEL;
MouseInput->ButtonData = (USHORT)(Scroll * -WHEEL_DELTA);
}
if (DevExt->MouseType == IntellimouseExplorer) {
if (Output & 16)
MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN;
if (Output & 32)
MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN;
}
I8042MouseHandleButtons(DevExt, MOUSE_LEFT_BUTTON_DOWN |
MOUSE_RIGHT_BUTTON_DOWN |
MOUSE_MIDDLE_BUTTON_DOWN |
MOUSE_BUTTON_4_DOWN |
MOUSE_BUTTON_5_DOWN);
I8042QueueMousePacket(DevExt->MouseObject);
DevExt->MouseState = MouseIdle;
break;
default:
DPRINT1("Unexpected state!\n");
}
}
BOOLEAN STDCALL I8042InterruptServiceMouse(struct _KINTERRUPT *Interrupt,
VOID *Context)
{
UCHAR Output, PortStatus;
NTSTATUS Status;
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context;
ULONG Iterations = 0;
do {
Status = I8042ReadStatus(&PortStatus);
Status = I8042ReadData(&Output);
Iterations++;
if (STATUS_SUCCESS == Status)
break;
KeStallExecutionProcessor(1);
} while (Iterations < DevExt->Settings.PollStatusIterations);
if (STATUS_SUCCESS != Status) {
DPRINT1("Spurious I8042 mouse interrupt\n");
return FALSE;
}
DPRINT("Got: %x\n", Output);
if (I8042PacketIsr(DevExt, Output)) {
if (DevExt->PacketComplete) {
DPRINT("Packet complete\n");
KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
}
DPRINT("Irq eaten by packet\n");
return TRUE;
}
I8042MouseInputTestTimeout(DevExt);
if (I8042MouseResetIsr(DevExt, PortStatus, &Output)) {
DPRINT("Handled by ResetIsr or hooked Isr\n");
if (NoChange != DevExt->MouseTimeoutState) {
KeInsertQueueDpc(&DevExt->DpcMouse, DevExt, NULL);
}
return TRUE;
}
if (DevExt->MouseType == Ps2pp)
I8042MouseHandlePs2pp(DevExt, Output);
else
I8042MouseHandle(DevExt, Output);
return TRUE;
}
VOID STDCALL I8042DpcRoutineMouse(PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2)
{
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1;
ULONG MouseTransferred = 0;
ULONG MouseInBufferCopy;
KIRQL Irql;
LARGE_INTEGER Timeout;
switch (DevExt->MouseTimeoutState) {
case TimeoutStart:
DevExt->MouseTimeoutState = NoChange;
if (DevExt->MouseTimeoutActive &&
!KeCancelTimer(&DevExt->TimerMouseTimeout)) {
/* The timer fired already, give up */
DevExt->MouseTimeoutActive = FALSE;
return;
}
Timeout.QuadPart = -15000000;
/* 1.5 seconds, should be enough */
KeSetTimer(&DevExt->TimerMouseTimeout,
Timeout,
&DevExt->DpcMouseTimeout);
DevExt->MouseTimeoutActive = TRUE;
return;
case TimeoutCancel:
DevExt->MouseTimeoutState = NoChange;
KeCancelTimer(&DevExt->TimerMouseTimeout);
DevExt->MouseTimeoutActive = FALSE;
default:
/* nothing, don't want a warning */ ;
}
/* Should be unlikely */
if (!DevExt->MouseComplete)
return;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->MouseComplete = FALSE;
MouseInBufferCopy = DevExt->MouseInBuffer;
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
DPRINT ("Send a mouse packet\n");
if (!DevExt->MouseData.ClassService)
return;
((PSERVICE_CALLBACK_ROUTINE) DevExt->MouseData.ClassService)(
DevExt->MouseData.ClassDeviceObject,
DevExt->MouseBuffer,
DevExt->MouseBuffer + MouseInBufferCopy,
&MouseTransferred);
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DevExt->MouseInBuffer -= MouseTransferred;
if (DevExt->MouseInBuffer)
RtlMoveMemory(DevExt->MouseBuffer,
DevExt->MouseBuffer+MouseTransferred,
DevExt->MouseInBuffer * sizeof(MOUSE_INPUT_DATA));
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
}
/* This timer DPC will be called when the mouse reset times out.
* I'll just send the 'disable mouse port' command to the controller
* and say the mouse doesn't exist.
*/
VOID STDCALL I8042DpcRoutineMouseTimeout(PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2)
{
PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)DeferredContext;
KIRQL Irql;
Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
DPRINT1("Mouse initialization timeout! (substate %x) "
"Disabling mouse.\n",
DevExt->MouseResetState);
if (!I8042MouseDisable(DevExt)) {
DPRINT1("Failed to disable mouse.\n");
}
DevExt->MouseExists = FALSE;
KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
}
/*
* Process the mouse internal device requests
* returns FALSE if it doesn't understand the
* call so someone else can handle it.
*/
#if 0
static BOOLEAN STDCALL I8042StartIoMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION Stk;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
Stk = IoGetCurrentIrpStackLocation(Irp);
switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
I8042StartPacket(
DevExt,
DeviceObject,
Stk->Parameters.DeviceIoControl.Type3InputBuffer,
Stk->Parameters.DeviceIoControl.InputBufferLength,
Irp);
break;
default:
return FALSE;
}
return TRUE;
}
#endif
/*
* Runs the mouse IOCTL_INTERNAL dispatch.
* Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request
* so someone else can have a try at it.
*/
NTSTATUS STDCALL I8042InternalDeviceControlMouse(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION Stk;
PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
DPRINT("InternalDeviceControl\n");
Irp->IoStatus.Information = 0;
Stk = IoGetCurrentIrpStackLocation(Irp);
switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_INTERNAL_MOUSE_CONNECT:
DPRINT("IOCTL_INTERNAL_MOUSE_CONNECT\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CONNECT_DATA)) {
DPRINT1("Mouse IOCTL_INTERNAL_MOUSE_CONNECT "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
goto intcontfailure;
}
if (!DevExt->MouseExists) {
Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
goto intcontfailure;
}
if (DevExt->MouseClaimed) {
DPRINT1("IOCTL_INTERNAL_MOUSE_CONNECT: "
"Mouse is already claimed\n");
Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
goto intcontfailure;
}
memcpy(&DevExt->MouseData,
Stk->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof(CONNECT_DATA));
DevExt->MouseHook.IsrWritePort = I8042IsrWritePortMouse;
DevExt->MouseHook.QueueMousePacket =
I8042QueueMousePacket;
DevExt->MouseHook.CallContext = DevExt;
{
PIO_WORKITEM WorkItem;
PI8042_HOOK_WORKITEM WorkItemData;
WorkItem = IoAllocateWorkItem(DeviceObject);
if (!WorkItem) {
DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: "
"Can't allocate work item\n");
Irp->IoStatus.Status =
STATUS_INSUFFICIENT_RESOURCES;
goto intcontfailure;
}
WorkItemData = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(I8042_HOOK_WORKITEM),
TAG_I8042);
if (!WorkItemData) {
DPRINT ("IOCTL_INTERNAL_MOUSE_CONNECT: "
"Can't allocate work item data\n");
Irp->IoStatus.Status =
STATUS_INSUFFICIENT_RESOURCES;
IoFreeWorkItem(WorkItem);
goto intcontfailure;
}
WorkItemData->WorkItem = WorkItem;
WorkItemData->Target =
DevExt->MouseData.ClassDeviceObject;
WorkItemData->Irp = Irp;
IoMarkIrpPending(Irp);
DevExt->MouseState = MouseResetting;
DevExt->MouseResetState = 1100;
IoQueueWorkItem(WorkItem,
I8042SendHookWorkItem,
DelayedWorkQueue,
WorkItemData);
Irp->IoStatus.Status = STATUS_PENDING;
}
break;
case IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER:
DPRINT("IOCTL_INTERNAL_I8042_MOUSE_WRITE_BUFFER\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
goto intcontfailure;
}
if (!DevExt->MouseInterruptObject) {
Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
goto intcontfailure;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
Irp->IoStatus.Status = STATUS_PENDING;
break;
case IOCTL_MOUSE_QUERY_ATTRIBUTES:
DPRINT("IOCTL_MOUSE_QUERY_ATTRIBUTES\n");
if (Stk->Parameters.DeviceIoControl.InputBufferLength <
sizeof(MOUSE_ATTRIBUTES)) {
DPRINT("Mouse IOCTL_MOUSE_QUERY_ATTRIBUTES "
"invalid buffer size\n");
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
goto intcontfailure;
}
memcpy(Irp->AssociatedIrp.SystemBuffer,
&DevExt->MouseAttributes,
sizeof(MOUSE_ATTRIBUTES));
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
default:
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
intcontfailure:
return Irp->IoStatus.Status;
}
BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt)
{
UCHAR Value;
NTSTATUS Status;
DPRINT("Enabling mouse\n");
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value &= ~(0x20); // don't disable mouse
Value |= 0x02; // enable mouse interrupts
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
return TRUE;
}
BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt)
{
UCHAR Value;
NTSTATUS Status;
DPRINT("Disabling mouse\n");
I8042Flush(); /* Just to be (kind of) sure */
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value |= 0x20; // don't disable mouse
Value &= ~(0x02); // enable mouse interrupts
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
I8042Flush();
/* Just to be (kind of) sure; if the mouse would
* say something while we are disabling it, these bytes would
* block the keyboard.
*/
return TRUE;
}
BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt)
{
BOOLEAN Ok = TRUE;
NTSTATUS Status;
UCHAR Value;
UCHAR ExpectedReply[] = { 0xFA, 0xAA, 0x00 };
unsigned ReplyByte;
if (! I8042Write(DevExt, I8042_CTRL_PORT, 0xD4) ||
! I8042Write(DevExt, I8042_DATA_PORT, 0xFF))
{
DPRINT1("Failed to write reset command to mouse\n");
Ok = FALSE;
}
for (ReplyByte = 0;
ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]) && Ok;
ReplyByte++)
{
Status = I8042ReadDataWait(DevExt, &Value);
if (! NT_SUCCESS(Status))
{
DPRINT1("No ACK after mouse reset, status 0x%08x\n",
Status);
Ok = FALSE;
}
else if (Value != ExpectedReply[ReplyByte])
{
DPRINT1("Unexpected reply: 0x%02x (expected 0x%02x)\n",
Value, ExpectedReply[ReplyByte]);
Ok = FALSE;
}
}
if (! Ok)
{
/* There is probably no mouse present. On some systems,
the probe locks the entire keyboard controller. Let's
try to get access to the keyboard again by sending a
reset */
I8042Flush();
I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST);
I8042ReadDataWait(DevExt, &Value);
I8042Flush();
}
DPRINT("Mouse %sdetected\n", Ok ? "" : "not ");
return Ok;
}

View file

@ -0,0 +1,137 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/ps2pp.c
* PURPOSE: i8042prt driver
* ps2pp protocol handling
* PROGRAMMER: Tinus
*/
/* INCLUDES ****************************************************************/
#ifndef NDEBUG
#define NDEBUG
#endif
#include <debug.h>
#include "i8042prt.h"
VOID I8042MouseHandlePs2pp(PDEVICE_EXTENSION DevExt, UCHAR Input)
{
UCHAR PktType;
PMOUSE_INPUT_DATA MouseInput = DevExt->MouseBuffer +
DevExt->MouseInBuffer;
/* First, collect 3 bytes for a packet
* We can detect out-of-sync only by checking
* the whole packet anyway.
*
* If bit 7 and 8 of the first byte are 0, its
* a normal packet.
*
* Otherwise, the packet is different, like this:
* 1: E 1 b3 b2 x x x x
* 2: x x b1 b0 x1 x0 1 0
* 3: x x x x x x x1 x0
*
* b3-0 form a code that specifies the packet type:
*
* 0 Device Type
* 1 Rollers and buttons
* 2 Reserved
* 3 Reserved
* 4 Device ID
* 5 Channel & Battery
* 6 Wireless notifications
* 7 Reserved
* 8 ShortID LSB (ShortID is a number that is supposed to differentiate
* 9 ShortID MSB between your mouse and your neighbours')
* 10 Reserved
* 11 Mouse capabilities
* 12 Remote control LSB
* 13 Remote control MSB
* 14 Reserved
* 15 Extended packet
*/
switch (DevExt->MouseState) {
case MouseIdle:
case XMovement:
DevExt->MouseLogiBuffer[DevExt->MouseState] = Input;
DevExt->MouseState++;
break;
case YMovement:
DevExt->MouseLogiBuffer[2] = Input;
DevExt->MouseState = MouseIdle;
/* first check if it's a normal packet */
if (!(DevExt->MouseLogiBuffer[0] & 0xC0)) {
DevExt->MouseState = MouseIdle;
I8042MouseHandle(DevExt, DevExt->MouseLogiBuffer[0]);
I8042MouseHandle(DevExt, DevExt->MouseLogiBuffer[1]);
I8042MouseHandle(DevExt, DevExt->MouseLogiBuffer[2]);
/* We could care about wether MouseState really
* advances, but we don't need to because we're
* only doing three bytes anyway, so the packet
* will never complete if it's broken.
*/
return;
}
/* sanity check */
if (((DevExt->MouseLogiBuffer[0] & 0x48) != 0x48) ||
(((DevExt->MouseLogiBuffer[1] & 0x0C) >> 2) !=
(DevExt->MouseLogiBuffer[2] & 0x03))) {
DPRINT1("Ps2pp packet fails sanity checks\n");
return;
}
/* Now get the packet type */
PktType = ((DevExt->MouseLogiBuffer[0] & 0x30) >> 4) &
((DevExt->MouseLogiBuffer[1] & 0x30) >> 6);
switch (PktType) {
case 0:
/* The packet contains the device ID, but we
* already read that in the initialization
* sequence. Ignore it.
*/
return;
case 1:
RtlZeroMemory(MouseInput, sizeof(MOUSE_INPUT_DATA));
if (DevExt->MouseLogiBuffer[2] & 0x10)
MouseInput->RawButtons |= MOUSE_BUTTON_4_DOWN;
if (DevExt->MouseLogiBuffer[2] & 0x20)
MouseInput->RawButtons |= MOUSE_BUTTON_5_DOWN;
if (DevExt->MouseLogiBuffer[2] & 0x0F) {
MouseInput->ButtonFlags |= MOUSE_WHEEL;
if (DevExt->MouseLogiBuffer[2] & 0x08)
MouseInput->ButtonData =
(DevExt->MouseLogiBuffer[2] & 0x07) -
8;
else
MouseInput->ButtonData =
DevExt->MouseLogiBuffer[2] & 0x07;
}
I8042MouseHandleButtons(DevExt, MOUSE_BUTTON_4_DOWN |
MOUSE_BUTTON_5_DOWN);
I8042QueueMousePacket(
DevExt->MouseObject);
return;
default:
/* These are for things that would probably
* be handled by logitechs own driver.
*/
return;
}
default:
DPRINT1("Unexpected input state for ps2pp!\n");
}
}

View file

@ -0,0 +1,211 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/input/i8042prt/registry.c
* PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
* Reading the registry
* PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
* Jason Filby (jasonfilby@yahoo.com)
* Tinus
*/
/* INCLUDES ****************************************************************/
#ifndef NDEBUG
#define NDEBUG
#endif
#include <debug.h>
#include "i8042prt.h"
/* FUNCTIONS *****************************************************************/
/*
* Read the registry keys associated with this device. The RegistryPath
* var is a hack. This should be more like what microsoft does, but I
* don't know exactly what they do except that it's a hack too...
*/
VOID STDCALL I8042ReadRegistry(PDRIVER_OBJECT DriverObject,
PDEVICE_EXTENSION DevExt)
{
RTL_QUERY_REGISTRY_TABLE Parameters[19];
NTSTATUS Status;
ULONG DefaultHeadless = 0;
ULONG DefaultCrashScroll = 0;
ULONG DefaultCrashSysRq = 0;
ULONG DefaultReportResetErrors = 0;
ULONG DefaultPollStatusIterations = 1;
ULONG DefaultResendIterations = 3;
ULONG DefaultPollingIterations = 12000;
ULONG DefaultPollingIterationsMaximum = 12000;
ULONG DefaultKeyboardDataQueueSize = 100;
ULONG DefaultOverrideKeyboardType = 0;
ULONG DefaultOverrideKeyboardSubtype = 0;
ULONG DefaultMouseDataQueueSize = 100;
ULONG DefaultMouseResendStallTime = 1000;
ULONG DefaultMouseSynchIn100ns = 20000000;
ULONG DefaultMouseResolution = 3;
ULONG DefaultSampleRate = 60;
ULONG DefaultNumberOfButtons = 2;
ULONG DefaultEnableWheelDetection = 1;
RtlZeroMemory(Parameters, sizeof(Parameters));
Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[0].Name = L"Headless";
Parameters[0].EntryContext = &DevExt->Settings.Headless;
Parameters[0].DefaultType = REG_DWORD;
Parameters[0].DefaultData = &DefaultHeadless;
Parameters[0].DefaultLength = sizeof(ULONG);
Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[1].Name = L"CrashOnCtrlScroll";
Parameters[1].EntryContext = &DevExt->Settings.CrashScroll;
Parameters[1].DefaultType = REG_DWORD;
Parameters[1].DefaultData = &DefaultCrashScroll;
Parameters[1].DefaultLength = sizeof(ULONG);
Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[2].Name = L"BreakOnSysRq";
Parameters[2].EntryContext = &DevExt->Settings.CrashSysRq;
Parameters[2].DefaultType = REG_DWORD;
Parameters[2].DefaultData = &DefaultCrashSysRq;
Parameters[2].DefaultLength = sizeof(ULONG);
Parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[3].Name = L"ReportResetErrors";
Parameters[3].EntryContext = &DevExt->Settings.ReportResetErrors;
Parameters[3].DefaultType = REG_DWORD;
Parameters[3].DefaultData = &DefaultReportResetErrors;
Parameters[3].DefaultLength = sizeof(ULONG);
Parameters[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[4].Name = L"PollStatusIterations";
Parameters[4].EntryContext = &DevExt->Settings.PollStatusIterations;
Parameters[4].DefaultType = REG_DWORD;
Parameters[4].DefaultData = &DefaultPollStatusIterations;
Parameters[4].DefaultLength = sizeof(ULONG);
Parameters[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[5].Name = L"ResendIterations";
Parameters[5].EntryContext = &DevExt->Settings.ResendIterations;
Parameters[5].DefaultType = REG_DWORD;
Parameters[5].DefaultData = &DefaultResendIterations;
Parameters[5].DefaultLength = sizeof(ULONG);
Parameters[6].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[6].Name = L"PollingIterations";
Parameters[6].EntryContext = &DevExt->Settings.PollingIterations;
Parameters[6].DefaultType = REG_DWORD;
Parameters[6].DefaultData = &DefaultPollingIterations;
Parameters[6].DefaultLength = sizeof(ULONG);
Parameters[7].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[7].Name = L"PollingIterationsMaximum";
Parameters[7].EntryContext = &DevExt->Settings.PollingIterationsMaximum;
Parameters[7].DefaultType = REG_DWORD;
Parameters[7].DefaultData = &DefaultPollingIterationsMaximum;
Parameters[7].DefaultLength = sizeof(ULONG);
Parameters[8].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[8].Name = L"KeyboardDataQueueSize";
Parameters[8].EntryContext =
&DevExt->KeyboardAttributes.InputDataQueueLength;
Parameters[8].DefaultType = REG_DWORD;
Parameters[8].DefaultData = &DefaultKeyboardDataQueueSize;
Parameters[8].DefaultLength = sizeof(ULONG);
Parameters[9].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[9].Name = L"OverrideKeyboardType";
Parameters[9].EntryContext = &DevExt->Settings.OverrideKeyboardType;
Parameters[9].DefaultType = REG_DWORD;
Parameters[9].DefaultData = &DefaultOverrideKeyboardType;
Parameters[9].DefaultLength = sizeof(ULONG);
Parameters[10].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[10].Name = L"OverrideKeyboardSubtype";
Parameters[10].EntryContext = &DevExt->Settings.OverrideKeyboardSubtype;
Parameters[10].DefaultType = REG_DWORD;
Parameters[10].DefaultData = &DefaultOverrideKeyboardSubtype;
Parameters[10].DefaultLength = sizeof(ULONG);
Parameters[11].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[11].Name = L"MouseDataQueueSize";
Parameters[11].EntryContext =
&DevExt->MouseAttributes.InputDataQueueLength;
Parameters[11].DefaultType = REG_DWORD;
Parameters[11].DefaultData = &DefaultMouseDataQueueSize;
Parameters[11].DefaultLength = sizeof(ULONG);
Parameters[12].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[12].Name = L"MouseResendStallTime";
Parameters[12].EntryContext = &DevExt->Settings.MouseResendStallTime;
Parameters[12].DefaultType = REG_DWORD;
Parameters[12].DefaultData = &DefaultMouseResendStallTime;
Parameters[12].DefaultLength = sizeof(ULONG);
Parameters[13].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[13].Name = L"MouseSynchIn100ns";
Parameters[13].EntryContext = &DevExt->Settings.MouseSynchIn100ns;
Parameters[13].DefaultType = REG_DWORD;
Parameters[13].DefaultData = &DefaultMouseSynchIn100ns;
Parameters[13].DefaultLength = sizeof(ULONG);
Parameters[14].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[14].Name = L"MouseResolution";
Parameters[14].EntryContext = &DevExt->Settings.MouseResolution;
Parameters[14].DefaultType = REG_DWORD;
Parameters[14].DefaultData = &DefaultMouseResolution;
Parameters[14].DefaultLength = sizeof(ULONG);
Parameters[15].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[15].Name = L"SampleRate";
Parameters[15].EntryContext = &DevExt->MouseAttributes.SampleRate;
Parameters[15].DefaultType = REG_DWORD;
Parameters[15].DefaultData = &DefaultSampleRate;
Parameters[15].DefaultLength = sizeof(ULONG);
Parameters[16].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[16].Name = L"NumberOfButtons";
Parameters[16].EntryContext = &DevExt->Settings.NumberOfButtons;
Parameters[16].DefaultType = REG_DWORD;
Parameters[16].DefaultData = &DefaultNumberOfButtons;
Parameters[16].DefaultLength = sizeof(ULONG);
Parameters[17].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[17].Name = L"EnableWheelDetection";
Parameters[17].EntryContext = &DevExt->Settings.EnableWheelDetection;
Parameters[17].DefaultType = REG_DWORD;
Parameters[17].DefaultData = &DefaultEnableWheelDetection;
Parameters[17].DefaultLength = sizeof(ULONG);
Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
I8042RegistryPath.Buffer,
Parameters,
NULL,
NULL);
if (!NT_SUCCESS(Status)) {
/* Actually, the defaults are not set when the function
* fails, as would happen during setup, so you have to
* set them manually anyway...
*/
RTL_QUERY_REGISTRY_TABLE *Current = Parameters;
DPRINT1 ("Can't read registry: %x\n", Status);
while (Current->Name) {
*((PULONG)Current->EntryContext) =
*((PULONG)Current->DefaultData);
Current++;
}
DPRINT1 ("Manually set defaults\n");
}
if (DevExt->Settings.MouseResolution > 3)
DevExt->Settings.MouseResolution = 3;
DPRINT("Done reading registry\n");
}