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;
|
||
}
|