680 lines
11 KiB
C
680 lines
11 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1993 Microsoft Corporation
|
|||
|
Copyright (c) 1993 Logitech Inc.
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
uart.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntddk.h"
|
|||
|
#include "uart.h"
|
|||
|
#include "sermouse.h"
|
|||
|
#include "debug.h"
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT,UARTSetFifo)
|
|||
|
#pragma alloc_text(INIT,UARTGetInterruptCtrl)
|
|||
|
#pragma alloc_text(INIT,UARTSetInterruptCtrl)
|
|||
|
#pragma alloc_text(INIT,UARTGetLineCtrl)
|
|||
|
#pragma alloc_text(INIT,UARTSetLineCtrl)
|
|||
|
#pragma alloc_text(INIT,UARTGetModemCtrl)
|
|||
|
#pragma alloc_text(INIT,UARTSetModemCtrl)
|
|||
|
#pragma alloc_text(INIT,UARTSetDlab)
|
|||
|
#pragma alloc_text(INIT,UARTGetBaudRate)
|
|||
|
#pragma alloc_text(INIT,UARTSetBaudRate)
|
|||
|
#pragma alloc_text(INIT,UARTGetState)
|
|||
|
#pragma alloc_text(INIT,UARTSetState)
|
|||
|
#pragma alloc_text(INIT,UARTReadChar)
|
|||
|
#pragma alloc_text(INIT,UARTIsTransmitEmpty)
|
|||
|
#pragma alloc_text(INIT,UARTWriteChar)
|
|||
|
#pragma alloc_text(INIT,UARTWriteString)
|
|||
|
#endif // ALLOC_PRAGMA
|
|||
|
|
|||
|
//
|
|||
|
// Constants
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UARTSetFifo(
|
|||
|
PUCHAR Port,
|
|||
|
UCHAR Value
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the FIFO register.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Value - The FIFO control mask.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_IIDR, Value);
|
|||
|
}
|
|||
|
UCHAR
|
|||
|
UARTGetInterruptCtrl(
|
|||
|
PUCHAR Port
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the serial port interrupt control register.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Serial port interrupt control register value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return READ_PORT_UCHAR(Port + ACE_IER);
|
|||
|
}
|
|||
|
|
|||
|
UCHAR
|
|||
|
UARTSetInterruptCtrl(
|
|||
|
PUCHAR Port,
|
|||
|
UCHAR Value
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the interrupt control register.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Value - The interrupt control mask.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Previous interrupt control value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UCHAR oldValue = UARTGetInterruptCtrl(Port);
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_IER, Value);
|
|||
|
|
|||
|
return oldValue;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
UCHAR
|
|||
|
UARTGetLineCtrl(
|
|||
|
PUCHAR Port
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the serial port line control register.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Serial port line control value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return READ_PORT_UCHAR(Port + ACE_LCR);
|
|||
|
}
|
|||
|
|
|||
|
UCHAR
|
|||
|
UARTSetLineCtrl(
|
|||
|
PUCHAR Port,
|
|||
|
UCHAR Value
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the serial port line control register.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Value - New line control value.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Previous serial line control register value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UCHAR oldValue = UARTGetLineCtrl(Port);
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_LCR, Value);
|
|||
|
|
|||
|
return oldValue;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
UCHAR
|
|||
|
UARTGetModemCtrl(
|
|||
|
PUCHAR Port
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the serial port modem control register.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Serial port modem control register value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return READ_PORT_UCHAR(Port + ACE_MCR);
|
|||
|
}
|
|||
|
|
|||
|
UCHAR
|
|||
|
UARTSetModemCtrl(
|
|||
|
PUCHAR Port,
|
|||
|
UCHAR Value
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the serial port modem control register.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Previous modem control register value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
UCHAR oldValue = UARTGetModemCtrl(Port);
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_MCR, Value);
|
|||
|
|
|||
|
return oldValue;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTSetDlab(
|
|||
|
PUCHAR Port,
|
|||
|
BOOLEAN Set
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set/reset the baud rate access bit.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Set - Set or Reset (TRUE/FALSE) the baud rate access bit.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The previous baud rate access bit setting.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UCHAR lineControl = UARTGetLineCtrl(Port);
|
|||
|
UCHAR newLineControl = Set ? lineControl | ACE_DLAB :
|
|||
|
lineControl & ~ACE_DLAB;
|
|||
|
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_LCR, newLineControl);
|
|||
|
|
|||
|
return lineControl & ACE_DLAB;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
UARTGetBaudRate(
|
|||
|
PUCHAR Port,
|
|||
|
ULONG BaudClock
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the serial port baud rate setting.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
BaudClock - The external frequency driving the serial chip.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Serial port baud rate.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
USHORT baudRateDivisor;
|
|||
|
ULONG baudRateFactor = BaudClock/BAUD_GENERATOR_DIVISOR;
|
|||
|
|
|||
|
//
|
|||
|
// Set the baud rate access bit.
|
|||
|
//
|
|||
|
|
|||
|
UARTSetDlab(Port, TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Read the baud rate factor.
|
|||
|
//
|
|||
|
|
|||
|
baudRateDivisor = READ_PORT_UCHAR(Port + ACE_DLL);
|
|||
|
baudRateDivisor |= READ_PORT_UCHAR(Port + ACE_DLM) << 8;
|
|||
|
|
|||
|
//
|
|||
|
// Reset the baud rate bit for normal data access.
|
|||
|
//
|
|||
|
|
|||
|
UARTSetDlab(Port, FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the divisor is not zero.
|
|||
|
//
|
|||
|
|
|||
|
if (baudRateDivisor == 0) {
|
|||
|
baudRateDivisor = 1;
|
|||
|
}
|
|||
|
|
|||
|
return baudRateFactor / baudRateDivisor;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
UARTSetBaudRate(
|
|||
|
PUCHAR Port,
|
|||
|
ULONG BaudRate,
|
|||
|
ULONG BaudClock
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the serial port baud rate.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
BaudRate - New serial port baud rate.
|
|||
|
|
|||
|
BaudClock - The external frequency driving the serial chip.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
ULONG baudRateFactor = BaudClock/BAUD_GENERATOR_DIVISOR;
|
|||
|
USHORT baudRateDivisor;
|
|||
|
|
|||
|
SerMouPrint((2, "SERMOUSE-SetBaudRate: Enter\n"));
|
|||
|
|
|||
|
baudRateDivisor = (USHORT) (baudRateFactor / BaudRate);
|
|||
|
UARTSetDlab(Port, TRUE);
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_DLL, (UCHAR)baudRateDivisor);
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_DLM, (UCHAR)(baudRateDivisor >> 8));
|
|||
|
UARTSetDlab(Port, FALSE);
|
|||
|
SerMouPrint((2, "SERMOUSE-New BaudRate: %u\n", BaudRate));
|
|||
|
|
|||
|
SerMouPrint((2, "SERMOUSE-SetBaudRate: Exit\n"));
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UARTGetState(
|
|||
|
PUCHAR Port,
|
|||
|
PUART Uart,
|
|||
|
ULONG BaudClock
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the complete state of the serial port. May be used for save/restore.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Uart - Pointer to a serial port structure.
|
|||
|
|
|||
|
BaudClock - The external frequency driving the serial chip.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
Uart->LineCtrl = UARTGetLineCtrl(Port);
|
|||
|
Uart->ModemCtrl = UARTGetModemCtrl(Port);
|
|||
|
Uart->InterruptCtrl = UARTGetInterruptCtrl(Port);
|
|||
|
Uart->BaudRate = UARTGetBaudRate(Port, BaudClock);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
UARTSetState(
|
|||
|
PUCHAR Port,
|
|||
|
PUART Uart,
|
|||
|
ULONG BaudClock
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Set the complete state of a serial port.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Uart - Pointer to a serial port structure.
|
|||
|
|
|||
|
BaudClock - The external frequency driving the serial chip.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UARTSetLineCtrl(Port, Uart->LineCtrl);
|
|||
|
UARTSetModemCtrl(Port, Uart->ModemCtrl);
|
|||
|
UARTSetInterruptCtrl(Port, Uart->InterruptCtrl);
|
|||
|
UARTSetBaudRate(Port, Uart->BaudRate, BaudClock);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTIsReceiveBufferFull(
|
|||
|
PUCHAR Port
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Check whether the serial port input buffer is full.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if a character is present in the input buffer, otherwise FALSE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return READ_PORT_UCHAR(Port + ACE_LSR) & ACE_DR;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTReadCharNoWait(
|
|||
|
PUCHAR Port,
|
|||
|
PUCHAR Value
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Read a character from the serial port and return immediately.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Value - The character read from the serial port input buffer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if character has been read, FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN charReady = FALSE;
|
|||
|
|
|||
|
if ( UARTIsReceiveBufferFull(Port) ) {
|
|||
|
*Value = READ_PORT_UCHAR(Port + ACE_RBR);
|
|||
|
charReady = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
return charReady;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTReadChar(
|
|||
|
PUCHAR Port,
|
|||
|
PUCHAR Value,
|
|||
|
ULONG Timeout
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Read a character from the serial port. Waits until a character has
|
|||
|
been read or the timeout value is reached.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Value - The character read from the serial port input buffer.
|
|||
|
|
|||
|
Timeout - The timeout value in milliseconds for the read.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if a character has been read, FALSE if a timeout occured.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
ULONG i, j;
|
|||
|
BOOLEAN returnValue = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Exit when a character is found or the timeout value is reached.
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; i < Timeout; i++) {
|
|||
|
for (j = 0; j < MS_TO_MICROSECONDS; j++) {
|
|||
|
if ((returnValue = UARTReadCharNoWait(Port, Value)) == TRUE) {
|
|||
|
|
|||
|
//
|
|||
|
// Got a character.
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Stall 1 microsecond and then try to read again.
|
|||
|
//
|
|||
|
|
|||
|
KeStallExecutionProcessor(1);
|
|||
|
}
|
|||
|
}
|
|||
|
if (returnValue) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return(returnValue);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTFlushReadBuffer(
|
|||
|
PUCHAR Port
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Flush the serial port input buffer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UCHAR value;
|
|||
|
|
|||
|
SerMouPrint((4, "SERMOUSE-UARTFlushReadBuffer: Enter\n"));
|
|||
|
while (UARTReadCharNoWait(Port, &value)) {
|
|||
|
/* Nothing */
|
|||
|
}
|
|||
|
SerMouPrint((4, "SERMOUSE-UARTFlushReadBuffer: Exit\n"));
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTIsTransmitEmpty(
|
|||
|
PUCHAR Port
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Check whether the serial port transmit buffer is empty.
|
|||
|
|
|||
|
Note: We also check whether the shift register is empty. This is
|
|||
|
not critical in our case, but allows some more delay between characters
|
|||
|
sent to a device. (Safe, safe...)
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if the serial port transmit buffer is empty.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return ((READ_PORT_UCHAR((PUCHAR) (Port + ACE_LSR)) &
|
|||
|
(ACE_TSRE | ACE_THRE)) == (ACE_THRE | ACE_TSRE));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTWriteChar(
|
|||
|
PUCHAR Port,
|
|||
|
UCHAR Value
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Write a character to a serial port. Make sure the transmit buffer
|
|||
|
is empty before we write there.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Value - Value to write to the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
while (!UARTIsTransmitEmpty(Port)) {
|
|||
|
/* Nothing */
|
|||
|
}
|
|||
|
WRITE_PORT_UCHAR(Port + ACE_THR, Value);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UARTWriteString(
|
|||
|
PUCHAR Port,
|
|||
|
PSZ Buffer
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Write a zero-terminated string to the serial port.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Port - Pointer to the serial port.
|
|||
|
|
|||
|
Buffer - Pointer to a zero terminated string to write to
|
|||
|
the serial port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSZ current = Buffer;
|
|||
|
|
|||
|
while (*current) {
|
|||
|
UARTWriteChar(Port, *current++);
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|