reactos/drivers/input/i8042prt/readwrite.c

224 lines
4.9 KiB
C

/*
* PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/input/i8042prt/readwrite.c
* PURPOSE: Read/write port functions
* PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com)
Copyright Jason Filby (jasonfilby@yahoo.com)
Copyright Martijn Vernooij (o112w8r02@sneakemail.com)
Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include "i8042prt.h"
#include <debug.h>
/* FUNCTIONS *****************************************************************/
VOID
i8042Flush(
IN PPORT_DEVICE_EXTENSION DeviceExtension)
{
UCHAR Ignore;
/* Flush output buffer */
while (NT_SUCCESS(i8042ReadData(DeviceExtension, KBD_OBF /* | MOU_OBF*/, &Ignore))) {
KeStallExecutionProcessor(50);
TRACE_(I8042PRT, "Output data flushed\n");
}
/* Flush input buffer */
while (NT_SUCCESS(i8042ReadData(DeviceExtension, KBD_IBF, &Ignore))) {
KeStallExecutionProcessor(50);
TRACE_(I8042PRT, "Input data flushed\n");
}
}
BOOLEAN
i8042IsrWritePort(
IN PPORT_DEVICE_EXTENSION DeviceExtension,
IN UCHAR Value,
IN UCHAR SelectCmd OPTIONAL)
{
if (SelectCmd)
if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, SelectCmd))
return FALSE;
return i8042Write(DeviceExtension, DeviceExtension->DataPort, Value);
}
/*
* FUNCTION: Read data from port 0x60
*/
NTSTATUS
i8042ReadData(
IN PPORT_DEVICE_EXTENSION DeviceExtension,
IN UCHAR StatusFlags,
OUT PUCHAR Data)
{
UCHAR PortStatus;
NTSTATUS Status;
Status = i8042ReadStatus(DeviceExtension, &PortStatus);
if (!NT_SUCCESS(Status))
return Status;
// If data is available
if (PortStatus & StatusFlags)
{
*Data = READ_PORT_UCHAR(DeviceExtension->DataPort);
INFO_(I8042PRT, "Read: 0x%02x (status: 0x%x)\n", Data[0], PortStatus);
// If the data is valid (not timeout, not parity error)
if ((PortStatus & KBD_PERR) == 0)
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
i8042ReadStatus(
IN PPORT_DEVICE_EXTENSION DeviceExtension,
OUT PUCHAR Status)
{
ASSERT(DeviceExtension->ControlPort != NULL);
*Status = READ_PORT_UCHAR(DeviceExtension->ControlPort);
return STATUS_SUCCESS;
}
/*
* FUNCTION: Read data from data port
*/
NTSTATUS
i8042ReadDataWait(
IN PPORT_DEVICE_EXTENSION DeviceExtension,
OUT PUCHAR Data)
{
ULONG Counter;
NTSTATUS Status;
Counter = DeviceExtension->Settings.PollingIterations;
while (Counter--)
{
Status = i8042ReadKeyboardData(DeviceExtension, Data);
if (NT_SUCCESS(Status))
return Status;
KeStallExecutionProcessor(50);
}
/* Timed out */
return STATUS_IO_TIMEOUT;
}
/*
* This one reads a value from the port; You don't have to specify
* which one, it'll always be from the one you talked to, so one function
* is enough this time. Note how MSDN specifies the
* WaitForAck parameter to be ignored.
*/
NTSTATUS NTAPI
i8042SynchReadPort(
IN PVOID Context,
OUT PUCHAR Value,
IN BOOLEAN WaitForAck)
{
PPORT_DEVICE_EXTENSION DeviceExtension;
UNREFERENCED_PARAMETER(WaitForAck);
DeviceExtension = (PPORT_DEVICE_EXTENSION)Context;
return i8042ReadDataWait(DeviceExtension, Value);
}
/*
* These functions are callbacks for filter driver custom
* initialization routines.
*/
NTSTATUS NTAPI
i8042SynchWritePort(
IN PPORT_DEVICE_EXTENSION DeviceExtension,
IN UCHAR Port,
IN UCHAR Value,
IN BOOLEAN WaitForAck)
{
NTSTATUS Status;
UCHAR Ack;
ULONG ResendIterations;
ResendIterations = DeviceExtension->Settings.ResendIterations + 1;
do
{
if (Port)
if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Port))
{
WARN_(I8042PRT, "Failed to write Port\n");
return STATUS_IO_TIMEOUT;
}
if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Value))
{
WARN_(I8042PRT, "Failed to write Value\n");
return STATUS_IO_TIMEOUT;
}
if (WaitForAck)
{
Status = i8042ReadDataWait(DeviceExtension, &Ack);
if (!NT_SUCCESS(Status))
{
WARN_(I8042PRT, "Failed to read Ack\n");
return Status;
}
if (Ack == KBD_ACK)
return STATUS_SUCCESS;
else if (Ack == KBD_RESEND)
INFO_(I8042PRT, "i8042 asks for a data resend\n");
}
else
{
return STATUS_SUCCESS;
}
TRACE_(I8042PRT, "Reiterating\n");
ResendIterations--;
} while (ResendIterations);
return STATUS_IO_TIMEOUT;
}
/*
* FUNCTION: Write data to a port, waiting first for it to become ready
*/
BOOLEAN
i8042Write(
IN PPORT_DEVICE_EXTENSION DeviceExtension,
IN PUCHAR addr,
IN UCHAR data)
{
ULONG Counter;
ASSERT(addr);
ASSERT(DeviceExtension->ControlPort != NULL);
Counter = DeviceExtension->Settings.PollingIterations;
while ((KBD_IBF & READ_PORT_UCHAR(DeviceExtension->ControlPort)) &&
(Counter--))
{
KeStallExecutionProcessor(50);
}
if (Counter)
{
WRITE_PORT_UCHAR(addr, data);
INFO_(I8042PRT, "Sent 0x%x to port %p\n", data, addr);
return TRUE;
}
return FALSE;
}