📄 pl010ser.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Module Name:
ser16550.c
Abstract:
This file implements the standard device specific functions for a 16550
based serial device.
Functions:
SL_Init2()
SL_PostInit()
SL_Deinit()
SL_Open()
SL_Close()
SL_ClearDTR()
SL_SetDTR()
SL_ClearRTS()
SL_SetRTS()
SL_ClearBreak()
SL_SetBreak()
SL_SetBaudRate()
SL_SetByteSize()
SL_SetParity()
SL_SetStopBits()
SL_GetRxBufferSize()
SL_GetRxStart()
SL_GetInterruptType()
SL_RxIntr()
SL_PutBytes()
SL_TxIntr()
SL_LineIntr()
SL_OtherIntr()
SL_GetStatus()
SL_Reset()
SL_GetModemStatus()
SL_PurgeComm()
SL_XmitComChar()
SL_PowerOff()
SL_PowerOn()
SL_SetDCB()
SL_SetCommTimeouts()
SL_Ioctl()
ReadRSR()
ReadFR()
DumpSerialRegisters()
LookUpValue()
DivisorOfRate()
Notes:
The RegCritSec is there to protect against non-atomic access of
register pairs. On a 16550, the main such collision comes from
the fact that THR and IER are overloaded as the DivLatch registers
and their mode is controlled via the LCR. So we need the
critical section protection around all access of these 3 registers.
But we also need to watch out for read/modify/write where we are
setting/ckearing a bit. In general, I just go ahead and acquire
the CS around all register accesses.
--*/
#include <windows.h>
#include <types.h>
#include <memory.h>
#include <serhw.h>
#include "pl010ser.h"
#include <ser16550.h>
#include "pl010com.h"
#include <hw16550.h>
#include <serdbg.h>
#include <excpt.h>
#include <hwdefs.h>
#include <clocks.h>
// Macros to read/write serial registers.
//#define INB(pInfo, reg) (READ_PORT_UCHAR((UCHAR *)((pInfo)->reg)))
//#define OUTB(pInfo, reg, value) (WRITE_PORT_UCHAR((UCHAR *)((pInfo)->reg), (unsigned char)(value)))
#define INB(pInfo, reg) (*((UCHAR *)((pInfo)->reg)))
#define OUTB(pInfo, reg, value) (*(UCHAR *)((pInfo)->reg) = (unsigned char)(value) )
BOOL SL_SetByteSize(PVOID pHead, ULONG ByteSize);
BOOL SL_SetStopBits(PVOID pHead, ULONG StopBits);
BOOL SL_SetParity(PVOID pHead, ULONG Parity);
#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION
__inline
VOID
ReadRSR(
PSER_INFO pHead
)
{
PAMBA_UART_INFO pSerAMBA = &(((PSER_INFO)pHead)->serAMBA);
PSER16550_INFO pSer16550 = &(((PSER_INFO)pHead)->ser16550);
ULONG LineEvents = 0;
try {
pSerAMBA->RSR = INB(pSerAMBA, pUART_RSR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
pSerAMBA->RSR = 0;
}
if ( pSerAMBA->RSR & (AMBA_UARTRSR_OE | AMBA_UARTRSR_PE | AMBA_UARTRSR_FE))
{
// Note: Its not wise to do debug msgs in here since they will
// pretty much guarantee that the FIFO gets overrun.
if ( pSerAMBA->RSR & AMBA_UARTRSR_OE )
{
// DEBUGMSG (ZONE_WARN, (TEXT("Overrun\r\n")));
pSer16550->DroppedBytes++;
pSer16550->CommErrors |= CE_OVERRUN;
// Clear state.
OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_OE);
}
if ( pSerAMBA->RSR & AMBA_UARTRSR_PE )
{
DEBUGMSG (ZONE_WARN, (TEXT("parity\r\n")));
pSer16550->CommErrors |= CE_RXPARITY;
// Clear state.
OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_PE);
}
if ( pSerAMBA->RSR & AMBA_UARTRSR_FE )
{
DEBUGMSG (ZONE_WARN, (TEXT("frame\r\n")));
pSer16550->CommErrors |= CE_FRAME;
// Clear state.
OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_FE);
}
LineEvents |= EV_ERR;
}
if ( pSerAMBA->RSR & AMBA_UARTRSR_BE )
{
LineEvents |= EV_BREAK;
// Clear state.
OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_BE);
}
// Let WaitCommEvent know about this error
if ( LineEvents )
pSer16550->EventCallback( pSer16550->pMddHead, LineEvents );
}
// The Integrator doesn't give us a delta bits for DCD, DSR, or CTS, so we need to
// figure out of they've changed since the last read, ourselves.
//
__inline
VOID
ReadFR(
PSER_INFO pHead
)
{
PAMBA_UART_INFO pSerAMBA = &(((PSER_INFO)pHead)->serAMBA);
PSER16550_INFO pSer16550 = &(((PSER_INFO)pHead)->ser16550);
ULONG Events = 0;
UCHAR fr = 0;
try {
fr = INB(pSerAMBA, pUART_FR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
fr = 0;
}
// For changes, we use callback to evaluate the event
if ((fr & AMBA_UARTFR_CTS) != (pSerAMBA->FR & AMBA_UARTFR_CTS))
{
Events |= EV_CTS;
}
if ((fr & AMBA_UARTFR_DSR) != (pSerAMBA->FR & AMBA_UARTFR_DSR))
{
Events |= EV_DSR;
}
if ((fr & AMBA_UARTFR_DCD) != (pSerAMBA->FR & AMBA_UARTFR_DCD))
{
Events |= EV_RLSD;
}
// Save the MSR value in a shadow
pSerAMBA->FR = fr;
// Clear modem status interrupt.
//
OUTB(pSerAMBA, pUART_ICR, 1);
if ( Events )
pSer16550->EventCallback( pSer16550->pMddHead, Events );
}
//
// Helper routine to search through a lookup table for a designated
// key.
//
ULONG
LookUpValue(
ULONG Key,
PLOOKUP_TBL pTbl,
PULONG pErrorCode
)
{
ULONG index = 0;
*pErrorCode = 0;
while ( index < pTbl->Size ) {
if ( Key == pTbl->Table[index].Key )
return(pTbl->Table[index].AssociatedValue);
++index;
}
*pErrorCode = (ULONG)-1;
return(0);
}
//
// Helper function. Pass in a baudrate, and the corresponding divisor
// (from the baudtable) is returned. If no matching baudrate is found
// in baudtable, then return 0.
//
USHORT
DivisorOfRate(
PVOID pHead, // @parm PVOID returned by HWinit.
ULONG BaudRate // @parm ULONG representing decimal baud rate.
)
{
ULONG errorcode = 0;
USHORT divisor = 0;
//divisor = (USHORT)LookUpValue(BaudRate,
// ((PSER_INFO)pHead)->ser16550.pBaudTable, &errorcode);
divisor = (USHORT) UART_DIVISOR(BaudRate);
return(divisor);
}
//
// This is a reverse lookup table which can be used to determine
// the FIFO trigger level from the 2 bit value stored in the FCR
//
#define HIGH_WATER_SIZE 4
static const
PAIRS HighWaterPairs[HIGH_WATER_SIZE] = {
{SERIAL_1_BYTE_HIGH_WATER, 0},
{SERIAL_4_BYTE_HIGH_WATER, 4},
{SERIAL_8_BYTE_HIGH_WATER, 8},
{SERIAL_14_BYTE_HIGH_WATER, 14}
};
static const
LOOKUP_TBL HighWaterTable = {HIGH_WATER_SIZE, (PAIRS *) HighWaterPairs};
#define CR_NORMAL_INTS (AMBA_UARTCR_RTIE | AMBA_UARTCR_RIE | AMBA_UARTCR_MSIE | AMBA_UARTCR_UARTEN)
// Routine to clear any pending interrupts. Called from Init and PostInit
// to make sure we atart out in a known state.
VOID
ClearPendingInts(
PVOID pHead // @parm PVOID returned by HWinit.
)
{
PSER16550_INFO pSer16550 = &(((PSER_INFO)pHead)->ser16550);
PAMBA_UART_INFO pSerAMBA = &(((PSER_INFO)pHead)->serAMBA);
EnterCriticalSection(&(pSer16550->RegCritSec));
try {
pSerAMBA->IIR = INB(pSerAMBA, pUART_IIR);
while ((pSerAMBA->IIR & AMBA_UARTIIR_RTIS) ||
(pSerAMBA->IIR & AMBA_UARTIIR_TIS) ||
(pSerAMBA->IIR & AMBA_UARTIIR_RIS) ||
(pSerAMBA->IIR & AMBA_UARTIIR_MIS))
{
DEBUGMSG (ZONE_INIT, (TEXT("!!IIR %X\r\n"), pSerAMBA->IIR));
// Clear ints.
//
// Read FIFO clean to deassert RIS and RTIS.
while(!(INB(pSerAMBA, pUART_FR) & AMBA_UARTFR_RXFE))
INB(pSerAMBA, pUART_DR);
// Clear modem status interrupts.
OUTB(pSerAMBA, pUART_ICR, 1);
}
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
DEBUGMSG (ZONE_ERROR,(TEXT("-SL_PostInit, 0x%X - ERROR\r\n"), pHead));
// Just fall through & release CritSec
}
LeaveCriticalSection(&(pSer16550->RegCritSec));
}
//
/////////////////// Start of exported entrypoints ////////////////
//
//
// @doc OEM
// @func PVOID | SL_Open | Configures 16550 for default behaviour.
//
VOID
SL_Open(
PVOID pHead // @parm PVOID returned by HWinit.
)
{
PSER16550_INFO pSer16550 = &(((PSER_INFO)pHead)->ser16550);
PAMBA_UART_INFO pSerAMBA = &(((PSER_INFO)pHead)->serAMBA);
DEBUGMSG (ZONE_OPEN,
(TEXT("+SL_Open 0x%X\r\n"), pHead));
// If the device is already open, all we do is increment count
if ( pSer16550->OpenCount++ ) {
DEBUGMSG (ZONE_OPEN,
(TEXT("-SL_Open 0x%X (%d opens)\r\n"),
pHead, pSer16550->OpenCount));
return ;
}
pSerAMBA->RSR = 0;
pSerAMBA->LCR_H = 0;
pSerAMBA->LCR_M = 0;
pSerAMBA->LCR_L = 0;
pSerAMBA->CR = 0;
pSerAMBA->FR = 0;
pSerAMBA->IIR = 0;
pSer16550->DroppedBytes = 0;
pSer16550->CTSFlowOff = FALSE; // Not flowed off yet
pSer16550->DSRFlowOff = FALSE; // Not flowed off yet
pSer16550->CommErrors = 0;
pSer16550->ModemStatus = 0;
EnterCriticalSection(&(pSer16550->RegCritSec));
try {
DEBUGMSG (ZONE_OPEN,
(TEXT("SL_Open Setting DCB parameters\r\n")));
// Get defaults from the DCB structure
SL_SetBaudRate( pHead, pSer16550->dcb.BaudRate );
SL_SetByteSize( pHead, pSer16550->dcb.ByteSize );
SL_SetStopBits( pHead, pSer16550->dcb.StopBits );
SL_SetParity( pHead, pSer16550->dcb.Parity );
// Set default data bits.
pSerAMBA->LCR_H = (AMBA_UARTLCR_H_WLEN_8 | AMBA_UARTLCR_H_FEN);
OUTB(pSerAMBA, pUART_LCR_H, pSerAMBA->LCR_H);
// For CE 3.0, we are still supporting
// the old style MDDs, and they don't call our PostInit, which
// needs to happen sometime prior to this. So for now, we go ahead
// ahead and clear out interrupts one last time. In 4.0, we can
// kill the old serial MDD and assume that everyone uses the new
// MDD and calls post init.
SL_PostInit(pHead);
ReadFR((PSER_INFO)pHead);
ReadRSR((PSER_INFO)pHead);
// Enable ints and UART.
pSerAMBA->CR = (CR_NORMAL_INTS | AMBA_UARTCR_TIE);
OUTB(pSerAMBA, pUART_CR, pSerAMBA->CR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
// Just get out of here.
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -