Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

679 lines
11 KiB

/*++
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;
}