mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-01 12:23:29 +00:00
Copy i8042prt driver from 0.3.1 branch to trunk. Try #2
svn path=/trunk/; revision=24379
This commit is contained in:
@@ -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.
|
||||
@@ -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 ([email protected])
|
||||
* Jason Filby ([email protected])
|
||||
* 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);
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 ([email protected])
|
||||
* Jason Filby ([email protected])
|
||||
* 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__ */
|
||||
}
|
||||
@@ -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 ([email protected])
|
||||
* Jason Filby ([email protected])
|
||||
* 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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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 ([email protected])
|
||||
* Jason Filby ([email protected])
|
||||
* 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");
|
||||
}
|
||||
Reference in New Issue
Block a user