📄 ser16550.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_Init()
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()
ReadLSR()
ReadMSR()
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 <nkintr.h>
#include <ceddk.h>
#include <memory.h>
#include <devload.h>
#include <ddkreg.h>
#include <serhw.h>
#include "..\isr16550\isr16550.h"
#include "ser16550.h"
#include "hw16550.h"
#include <serdbg.h>
#include <excpt.h>
#ifndef _PREFAST_
#pragma warning(disable: 4068) // Disable pragma warnings
#endif
// 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)))
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
//
// Reading the LSR clears most of its bits. So, we provide this wrapper,
// which reads the register, records any interesting values, and
// stores the current LSR contents in the shadow register.
//
__inline
VOID ProcessLSR (PSER16550_INFO pHWHead)
{
ULONG LineEvents = 0;
if ( pHWHead->LSR & (SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE)) {
// Note: Its not wise to do debug msgs in here since they will
// pretty much guarantee that the FIFO gets overrun.
if ( pHWHead->LSR & SERIAL_LSR_OE ) {
// DEBUGMSG (ZONE_WARN, (TEXT("Overrun\r\n")));
pHWHead->DroppedBytes++;
pHWHead->CommErrors |= CE_OVERRUN;
}
if ( pHWHead->LSR & SERIAL_LSR_PE ) {
// DEBUGMSG (ZONE_WARN, (TEXT("parity\r\n")));
pHWHead->CommErrors |= CE_RXPARITY;
}
if ( pHWHead->LSR & SERIAL_LSR_FE ) {
// DEBUGMSG (ZONE_WARN, (TEXT("frame\r\n")));
pHWHead->CommErrors |= CE_FRAME;
}
LineEvents |= EV_ERR;
}
if ( pHWHead->LSR & SERIAL_LSR_BI )
LineEvents |= EV_BREAK;
// Let WaitCommEvent know about this error
if ( LineEvents )
pHWHead->EventCallback( pHWHead->pMddHead, LineEvents );
}
__inline
VOID
ReadLSR(
PSER16550_INFO pHWHead
)
{
try {
pHWHead->LSR = INB(pHWHead, pLSR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
pHWHead->LSR = SERIAL_LSR_THRE;
}
ProcessLSR (pHWHead);
}
//
// Reading the MSR clears many of its bits. So, we provide this wrapper,
// which reads the register, records any interesting values, and
// stores the current MSR contents in the shadow register.
// Note that we always have DDCD and DCTS enabled, so if someone
// wants to keep an eye on these lines, its OK to simply read the
// shadow register, since if the value changes, the interrupt
// will cause the shadow to be updated.
//
__inline
VOID ProcessMSR (PSER16550_INFO pHWHead)
{
ULONG Events = 0;
// For changes, we use callback to evaluate the event
if (pHWHead->MSR & SERIAL_MSR_DCTS)
Events |= EV_CTS;
if ( pHWHead->MSR & SERIAL_MSR_DDSR )
Events |= EV_DSR;
if ( pHWHead->MSR & SERIAL_MSR_TERI )
Events |= EV_RING;
if ( pHWHead->MSR & SERIAL_MSR_DDCD )
Events |= EV_RLSD;
if ( Events )
pHWHead->EventCallback( pHWHead->pMddHead, Events );
}
__inline
VOID
ReadMSR(
PSER16550_INFO pHWHead
)
{
UCHAR msr;
try {
msr = INB(pHWHead, pMSR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
msr = 0;
}
// Save the MSR value in a shadow
pHWHead->MSR = msr;
ProcessMSR (pHWHead);
}
#ifdef DEBUG
//
// This routine is used only for debugging, and performs a formatted
// ascii dump of the various UART registers.
//
VOID
DumpSerialRegisters(
PVOID pHead
)
{
UINT8 byte;
PSER16550_INFO pHWHead = (PSER16550_INFO)pHead;
#pragma prefast(disable: 322, "Recover gracefully from hardware failure")
try {
ReadLSR( pHWHead );
byte = pHWHead->LSR;
NKDbgPrintfW(TEXT("16550 lsr: \t%2.2X\t"), byte);
if ( byte & SERIAL_LSR_DR )
NKDbgPrintfW(TEXT("DataReady "));
if ( byte & SERIAL_LSR_OE )
NKDbgPrintfW(TEXT("OverRun "));
if ( byte & SERIAL_LSR_PE )
NKDbgPrintfW(TEXT("ParityErr "));
if ( byte & SERIAL_LSR_FE )
NKDbgPrintfW(TEXT("FramingErr "));
if ( byte & SERIAL_LSR_BI )
NKDbgPrintfW(TEXT("BreakIntpt "));
if ( byte & SERIAL_LSR_THRE )
NKDbgPrintfW(TEXT("THREmpty "));
if ( byte & SERIAL_LSR_TEMT )
NKDbgPrintfW(TEXT("TXEmpty "));
if ( byte & SERIAL_LSR_FIFOERR )
NKDbgPrintfW(TEXT("FIFOErr "));
NKDbgPrintfW(TEXT("\r\n"));
byte = INB(pHWHead, pData);
NKDbgPrintfW(TEXT("16550 rbr/thr:\t%2.2X\r\n"), byte);
byte = INB(pHWHead, pIER);
NKDbgPrintfW(TEXT("16550 IER: \t%2.2X\t"), byte);
if ( byte & SERIAL_IER_RDA )
NKDbgPrintfW(TEXT("RXData "));
if ( byte & SERIAL_IER_THR )
NKDbgPrintfW(TEXT("TXData "));
if ( byte & SERIAL_IER_RLS )
NKDbgPrintfW(TEXT("RXErr "));
if ( byte & SERIAL_IER_MS )
NKDbgPrintfW(TEXT("ModemStatus "));
NKDbgPrintfW(TEXT("\r\n"));
byte = INB(pHWHead, pIIR_FCR);
NKDbgPrintfW(TEXT("16550 iir: \t%2.2X\t"), byte);
if ( byte & SERIAL_IIR_RDA )
NKDbgPrintfW(TEXT("RXData "));
if ( byte & SERIAL_IIR_THRE )
NKDbgPrintfW(TEXT("TXData "));
if ( byte & SERIAL_IIR_RLS )
NKDbgPrintfW(TEXT("RXErr "));
if ( (byte & SERIAL_IIR_CTI) == SERIAL_IIR_CTI )
NKDbgPrintfW(TEXT("CTI "));
if ( byte == SERIAL_IIR_MS )
NKDbgPrintfW(TEXT("ModemStatus "));
if ( byte & 0x01 )
NKDbgPrintfW(TEXT("IntPending "));
NKDbgPrintfW(TEXT("\r\n"));
byte = INB(pHWHead, pLCR);
NKDbgPrintfW(TEXT("16550 lcr: \t%2.2X\t"), byte);
NKDbgPrintfW(TEXT("%dBPC "), ((byte & 0x03)+5) );
if ( byte & SERIAL_LCR_DLAB )
NKDbgPrintfW(TEXT("DLAB "));
if ( byte & SERIAL_LCR_DLAB )
NKDbgPrintfW(TEXT("Break "));
NKDbgPrintfW(TEXT("\r\n"));
byte = INB(pHWHead, pMCR);
NKDbgPrintfW(TEXT("16550 mcr: \t%2.2X\t"), byte);
if ( byte & SERIAL_MCR_DTR )
NKDbgPrintfW(TEXT("DTR "));
if ( byte & SERIAL_MCR_RTS )
NKDbgPrintfW(TEXT("RTS "));
if ( byte & SERIAL_MCR_OUT1 )
NKDbgPrintfW(TEXT("OUT1 "));
if ( byte & SERIAL_MCR_OUT2 )
NKDbgPrintfW(TEXT("OUT2 "));
if ( byte & SERIAL_MCR_LOOP )
NKDbgPrintfW(TEXT("LOOP "));
NKDbgPrintfW(TEXT("\r\n"));
ReadMSR( pHWHead );
byte = pHWHead->MSR;
NKDbgPrintfW(TEXT("16550 msr: \t%2.2X\t"), byte);
if ( byte & SERIAL_MSR_DCTS )
NKDbgPrintfW(TEXT("DCTS "));
if ( byte & SERIAL_MSR_DDSR )
NKDbgPrintfW(TEXT("DDSR "));
if ( byte & SERIAL_MSR_TERI )
NKDbgPrintfW(TEXT("TERI "));
if ( byte & SERIAL_MSR_DDCD )
NKDbgPrintfW(TEXT("DDCD"));
if ( byte & SERIAL_MSR_CTS )
NKDbgPrintfW(TEXT(" CTS"));
if ( byte & SERIAL_MSR_DSR )
NKDbgPrintfW(TEXT("DSR "));
if ( byte & SERIAL_MSR_RI )
NKDbgPrintfW(TEXT("RI "));
if ( byte & SERIAL_MSR_DCD )
NKDbgPrintfW(TEXT("DCD "));
NKDbgPrintfW(TEXT("\r\n"));
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
// Nothing much to clean up here.
}
#pragma prefast(pop)
}
#endif // DEBUG
//
// 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);
}
#define BAUD_TABLE_SIZE 23
static const
PAIRS LS_BaudPairs[BAUD_TABLE_SIZE] = {
{50, 2307},
{75, 1538},
{110, 1049},
{135, 858},
{150, 769},
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -