reactos/drivers/input/inport/hardware.c

441 lines
14 KiB
C
Raw Permalink Normal View History

/*
* PROJECT: ReactOS InPort (Bus) Mouse Driver
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Hardware support code
* COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com)
*/
/* Note: Some code was taken from Linux */
/* INCLUDES *******************************************************************/
#include "inport.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
#define READ_MOUSE(DeviceExtension, Port) \
READ_PORT_UCHAR((DeviceExtension)->IoBase + (Port))
#define WRITE_MOUSE(DeviceExtension, Port, Data) \
WRITE_PORT_UCHAR((DeviceExtension)->IoBase + (Port), (Data))
/*
* NEC
*/
#define NEC_BM_DATA 0x00
#define NEC_BM_CONTROL 0x04
#define NEC_INT_ENABLE 0x00
#define NEC_INT_DISABLE 0x10
#define NEC_READ_X_LOW 0x00
#define NEC_READ_X_HIGH 0x20
#define NEC_READ_Y_LOW 0x40
#define NEC_READ_Y_HIGH 0x60
#define NEC_INPUT_CAPTURE 0x00
#define NEC_INPUT_HOLD 0x80
#define NEC_BM_CONFIG 0x06
#define NEC_PPI_INT_ENABLE 0x08
#define NEC_PPI_INT_DISABLE 0x09
#define NEC_PPI_HC_NO_CLEAR 0x0E
#define NEC_PPI_HC_CLEAR 0x0F
#define NEC_PPI_DEFAULT_MODE 0x93
#define NEC_BM_INT_RATE 0x4002
#define NEC_RATE_120_HZ 0x00
#define NEC_RATE_60_HZ 0x01
#define NEC_RATE_30_HZ 0x02
#define NEC_RATE_15_HZ 0x03
#define NEC_BM_HIRESO_BASE (PUCHAR)0x61
#define NEC_BUTTON_RIGHT 0x20
#define NEC_BUTTON_LEFT 0x80
/*
* Microsoft InPort
*/
#define MS_INPORT_CONTROL 0x00
#define INPORT_REG_BTNS 0x00
#define INPORT_REG_X 0x01
#define INPORT_REG_Y 0x02
#define INPORT_REG_MODE 0x07
#define INPORT_RESET 0x80
#define MS_INPORT_DATA 0x01
#define INPORT_MODE_IRQ 0x01
#define INPORT_MODE_BASE 0x10
#define INPORT_MODE_HOLD 0x20
#define INPORT_HAS_MOVED 0x40
#define MS_INPORT_SIGNATURE 0x02
#define MS_BUTTON_RIGHT 0x01
#define MS_BUTTON_MIDDLE 0x02
#define MS_BUTTON_LEFT 0x04
/*
* Logitech
*/
#define LOG_BM_DATA 0x00
#define LOG_BM_SIGNATURE 0x01
#define LOG_SIGNATURE_BYTE 0xA5
#define LOG_BM_CONTROL 0x02
#define LOG_ENABLE_IRQ 0x00
#define LOG_DISABLE_IRQ 0x10
#define LOG_READ_X_LOW 0x80
#define LOG_READ_X_HIGH 0xA0
#define LOG_READ_Y_LOW 0xC0
#define LOG_READ_Y_HIGH 0xE0
#define LOG_BM_CONFIG 0x03
#define LOG_DEFAULT_MODE 0x90
#define LOG_CONFIG_BYTE 0x91
#define LOG_BUTTON_RIGHT 0x20
#define LOG_BUTTON_MIDDLE 0x40
#define LOG_BUTTON_LEFT 0x80
/* FUNCTIONS ******************************************************************/
VOID
NTAPI
InPortDpcForIsr(
_In_ PKDPC Dpc,
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp,
_In_opt_ PVOID Context)
{
PINPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
ULONG DummyInputDataConsumed;
INPORT_RAW_DATA RawData;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(Irp);
UNREFERENCED_PARAMETER(Context);
/* Copy raw data */
OldIrql = KeAcquireInterruptSpinLock(DeviceExtension->InterruptObject);
RawData = DeviceExtension->RawData;
KeReleaseInterruptSpinLock(DeviceExtension->InterruptObject, OldIrql);
/* Fill out fields */
DeviceExtension->MouseInputData.LastX = RawData.DeltaX;
DeviceExtension->MouseInputData.LastY = RawData.DeltaY;
DeviceExtension->MouseInputData.ButtonFlags = 0;
if (RawData.ButtonDiff != 0)
{
switch (DeviceExtension->MouseType)
{
case NecBusMouse:
{
if (RawData.ButtonDiff & NEC_BUTTON_LEFT)
{
if (RawData.Buttons & NEC_BUTTON_LEFT)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
}
if (RawData.ButtonDiff & NEC_BUTTON_RIGHT)
{
if (RawData.Buttons & NEC_BUTTON_RIGHT)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
}
break;
}
case MsInPortMouse:
{
/* Button flags have to be inverted */
if (RawData.ButtonDiff & MS_BUTTON_LEFT)
{
if (RawData.Buttons & MS_BUTTON_LEFT)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
}
if (RawData.ButtonDiff & MS_BUTTON_RIGHT)
{
if (RawData.Buttons & MS_BUTTON_RIGHT)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
}
if (RawData.ButtonDiff & MS_BUTTON_MIDDLE)
{
if (RawData.Buttons & MS_BUTTON_MIDDLE)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
}
break;
}
case LogitechBusMouse:
{
if (RawData.ButtonDiff & LOG_BUTTON_LEFT)
{
if (RawData.Buttons & LOG_BUTTON_LEFT)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
}
if (RawData.ButtonDiff & LOG_BUTTON_RIGHT)
{
if (RawData.Buttons & LOG_BUTTON_RIGHT)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
}
if (RawData.ButtonDiff & LOG_BUTTON_MIDDLE)
{
if (RawData.Buttons & LOG_BUTTON_MIDDLE)
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
else
DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
}
break;
}
}
}
/* Send mouse packet */
(*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(
DeviceExtension->ClassDeviceObject,
&DeviceExtension->MouseInputData,
&DeviceExtension->MouseInputData + 1,
&DummyInputDataConsumed);
}
BOOLEAN
NTAPI
InPortIsr(
_In_ PKINTERRUPT Interrupt,
_In_ PVOID Context)
{
UCHAR Buttons;
ULONG ButtonDiff;
CHAR DeltaX, DeltaY;
PINPORT_DEVICE_EXTENSION DeviceExtension = Context;
UNREFERENCED_PARAMETER(Interrupt);
switch (DeviceExtension->MouseType)
{
case NecBusMouse:
{
WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
NEC_INPUT_CAPTURE | NEC_INT_DISABLE);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_X_LOW);
DeltaX = READ_MOUSE(DeviceExtension, NEC_BM_DATA) & 0x0F;
WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_X_HIGH);
DeltaX |= READ_MOUSE(DeviceExtension, NEC_BM_DATA) << 4;
WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_Y_LOW);
DeltaY = READ_MOUSE(DeviceExtension, NEC_BM_DATA) & 0x0F;
WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_Y_HIGH);
Buttons = READ_MOUSE(DeviceExtension, NEC_BM_DATA);
DeltaY |= Buttons << 4;
Buttons &= (NEC_BUTTON_LEFT | NEC_BUTTON_RIGHT);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
NEC_INPUT_HOLD | NEC_INT_ENABLE);
break;
}
case MsInPortMouse:
{
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE);
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_BTNS);
Buttons = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
if (Buttons & INPORT_HAS_MOVED)
{
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_X);
DeltaX = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_Y);
DeltaY = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
}
else
{
DeltaX = 0;
DeltaY = 0;
}
Buttons &= (MS_BUTTON_MIDDLE | MS_BUTTON_LEFT | MS_BUTTON_RIGHT);
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
INPORT_MODE_IRQ | INPORT_MODE_BASE);
break;
}
case LogitechBusMouse:
{
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_X_LOW);
DeltaX = READ_MOUSE(DeviceExtension, LOG_BM_DATA) & 0x0F;
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_X_HIGH);
DeltaX |= READ_MOUSE(DeviceExtension, LOG_BM_DATA) << 4;
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_Y_LOW);
DeltaY = READ_MOUSE(DeviceExtension, LOG_BM_DATA) & 0x0F;
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_Y_HIGH);
Buttons = READ_MOUSE(DeviceExtension, LOG_BM_DATA);
DeltaY |= Buttons << 4;
Buttons &= (LOG_BUTTON_RIGHT | LOG_BUTTON_MIDDLE | LOG_BUTTON_LEFT);
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_ENABLE_IRQ);
break;
}
}
ButtonDiff = DeviceExtension->MouseButtonState ^ Buttons;
DeviceExtension->MouseButtonState = Buttons;
/*
* Bus mouse devices don't have a status register to check
* whether this interrupt is indeed for us.
*/
if ((DeltaX == 0) && (DeltaY == 0) && (ButtonDiff == 0))
{
/* We just pretend that the interrupt is not ours */
return FALSE;
}
else
{
DeviceExtension->RawData.DeltaX = DeltaX;
DeviceExtension->RawData.DeltaY = DeltaY;
DeviceExtension->RawData.Buttons = Buttons;
DeviceExtension->RawData.ButtonDiff = ButtonDiff;
IoRequestDpc(DeviceExtension->Self, NULL, NULL);
return TRUE;
}
}
CODE_SEG("PAGE")
VOID
NTAPI
InPortInitializeMouse(
_In_ PINPORT_DEVICE_EXTENSION DeviceExtension)
{
PAGED_CODE();
/* Initialize mouse and disable interrupts */
switch (DeviceExtension->MouseType)
{
case NecBusMouse:
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_DEFAULT_MODE);
/* Setup interrupt rate (unavailable on hireso machines) */
if (DeviceExtension->IoBase != NEC_BM_HIRESO_BASE)
WRITE_MOUSE(DeviceExtension, NEC_BM_INT_RATE, NEC_RATE_60_HZ);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_DISABLE);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
break;
case MsInPortMouse:
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_RESET);
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA, INPORT_MODE_BASE);
break;
case LogitechBusMouse:
WRITE_MOUSE(DeviceExtension, LOG_BM_CONFIG, LOG_DEFAULT_MODE);
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_DISABLE_IRQ);
break;
}
}
BOOLEAN
NTAPI
InPortStartMouse(
_In_ PVOID SynchronizeContext)
{
PINPORT_DEVICE_EXTENSION DeviceExtension = SynchronizeContext;
/* Enable interrupts */
switch (DeviceExtension->MouseType)
{
case NecBusMouse:
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_ENABLE);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
break;
case MsInPortMouse:
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
INPORT_MODE_IRQ | INPORT_MODE_BASE);
break;
case LogitechBusMouse:
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_ENABLE_IRQ);
break;
}
return TRUE;
}
BOOLEAN
NTAPI
InPortStopMouse(
_In_ PVOID SynchronizeContext)
{
PINPORT_DEVICE_EXTENSION DeviceExtension = SynchronizeContext;
/* Disable interrupts */
switch (DeviceExtension->MouseType)
{
case NecBusMouse:
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_DISABLE);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
break;
case MsInPortMouse:
WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA, INPORT_MODE_BASE);
break;
case LogitechBusMouse:
WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_DISABLE_IRQ);
break;
}
return TRUE;
}