📄 ser16950.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.
Copyright (c) 1999 Socket Communications
Module Name:
ser16950.c
Abstract:
This file implements the standard device specific functions for a 16C950
based serial device.
Functions:
SL_Init()
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:
--*/
#include <windows.h>
#include <types.h>
#include <ceddk.h>
#include <memory.h>
#include <serhw.h>
#include <cardserv.h>
#include <sockserv.h>
#include <tuple.h>
#include "ser16950.h"
#include "hw16550.h"
#include <serdbg.h>
#include <excpt.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) (*((pInfo)->reg))
//#define OUTB(pInfo, reg, value) (*((pInfo)->reg)) = ((unsigned char)(value))
// Macros to read/write hardware registers in the mapped I/O space.
#define VOLB *(volatile BYTE* const)
//#define INB(pClientContext, regOffset) \
// (VOLB(((pClientContext)->regOffset)))
//#define OUTB(pClientContext, regOffset, value) \
// (VOLB((pClientContext)->regOffset) = (UCHAR)(value))
// Some forward references...
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
#ifdef DEBUG
#pragma message ("Compile DEBUG is on:WendySer[ser16950.c]")
#endif
//
// Define SLIP wrapper values
//
#define SLIP_END 0xc0
#define SLIP_ESC 0xdb
#define SLIP_ESC_END 0xdc
#define SLIP_ESC_ESC 0xdd
//
// 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
UINT8
ReadLSR(
PSER16950_INFO pHWHead
)
{
ULONG LineEvents = 0;
try
{
pHWHead->LSR = INB(pHWHead, pLSR);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
pHWHead->LSR = SERIAL_LSR_THRE;
}
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 );
return pHWHead->LSR;
}
//
// 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
ReadMSR(
PSER16950_INFO pHWHead
)
{
ULONG Events = 0;
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;
// For changes, we use callback to evaluate the event
if (msr & SERIAL_MSR_DCTS)
Events |= EV_CTS;
if( msr & SERIAL_MSR_DDSR )
Events |= EV_DSR;
if ( msr & SERIAL_MSR_TERI )
Events |= EV_RING;
if ( msr & SERIAL_MSR_DDCD )
Events |= EV_RLSD;
if( Events )
pHWHead->EventCallback( pHWHead->pMddHead, Events );
}
#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;
PSER16950_INFO pHWHead = (PSER16950_INFO)pHead;
try
{
byte = ReadLSR( pHWHead );
NKDbgPrintfW(TEXT("16950 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("16950 rbr/thr:\t%2.2X\r\n"), byte);
byte = INB(pHWHead, pIER);
NKDbgPrintfW(TEXT("16950 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("16950 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("16950 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("16950 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 "));
if( byte & MCR_BAUD_PRESCALE )
NKDbgPrintfW(TEXT("PRESCALE "));
NKDbgPrintfW(TEXT("\r\n"));
ReadMSR( pHWHead );
byte = pHWHead->MSR;
NKDbgPrintfW(TEXT("16950 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.
}
}
#endif // DEBUG
//
// Helper routine to search through a lookup table for a designated
// key.
//
BOOL
LookUpValue(
ULONG Key,
PLOOKUP_TBL_4 pTbl,
PULONG pdivisor,
PULONG pscaler,
PULONG ptimesclock
)
{
ULONG index = 0;
ULONG errorcode = 0;
while ( index < pTbl->Size )
{
if ( Key == pTbl->Table[index].Key )
{
*pdivisor = pTbl->Table[index].AssociatedValue;
*pscaler = pTbl->Table[index].AssociatedValue2;
*ptimesclock = pTbl->Table[index].AssociatedValue3;
return TRUE;
}
++index;
}
return FALSE;
}
//
// 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.
//
#define DIVISOROFRATE(pHead, BaudRate, pdivisor, pscaler, ptimesclock) \
LookUpValue(BaudRate,(PLOOKUP_TBL_4)((PSER16950_INFO) pHead)->pBaudTable, pdivisor, pscaler, ptimesclock)
#define IER_NORMAL_INTS (SERIAL_IER_RDA | SERIAL_IER_RLS | SERIAL_IER_MS)
//
/////////////////// Start of exported entrypoints ////////////////
//
//
// @doc OEM
// @func PVOID | SL_Open | Configures 16950 for default behaviour.
//
VOID
SL_Open(
PVOID pHead // @parm PVOID returned by HWinit.
)
{
PSER16950_INFO pHWHead = (PSER16950_INFO)pHead;
DEBUGMSG (ZONE_OPEN,
(TEXT("+SL_Open 0x%X\r\n"), pHead));
// If the device is already open, all we do is increment count
if( pHWHead->OpenCount++ )
{
DEBUGMSG (ZONE_OPEN,
(TEXT("-SL_Open 0x%X (%d opens)\r\n"),
pHead, pHWHead->OpenCount));
return ;
}
pHWHead->FCR = 0;
pHWHead->IER = 0;
pHWHead->IIR = 0;
pHWHead->LSR = 0;
pHWHead->MSR = 0;
pHWHead->mimicACR = 0;
pHWHead->DroppedBytes = 0;
pHWHead->CTSFlowOff = FALSE; // Not flowed off yet
pHWHead->DSRFlowOff = FALSE; // Not flowed off yet
pHWHead->RINGFlowOff = FALSE; // Not flowed off yet
pHWHead->SLIPStartOfPacket = FALSE;
pHWHead->SLIPEndOfPacket = FALSE;
pHWHead->SLIPEscapeFound = FALSE;
pHWHead->RIToggleCount = 0;
pHWHead->RIToggleTimeout = 0;
pHWHead->CommErrors = 0;
pHWHead->ModemStatus = 0;
pHWHead->bSleepDisable = 0;
// Setup some defaults...
pHWHead->RxFifoTrigger = SERIAL_BYTE_RX_HIGH_WATER;
pHWHead->TxFifoTrigger = SERIAL_BYTE_TX_LOW_WATER;
try
{
pHWHead->ChipID = CHIP_ID_WENDY1;
pHWHead->IER = IER_NORMAL_INTS; // Shadow the IER
OUTB(pHWHead, pIER, pHWHead->IER);
pHWHead->MCR = INB(pHWHead, pMCR) & 0xF0; // Only take state of upper 4 bits
pHWHead->MCR |= SERIAL_MCR_IRQ_ENABLE;
OUTB(pHWHead, pMCR, pHWHead->MCR);
// Set default framing bits.
//pHWHead->LCR = SERIAL_8_DATA | SERIAL_1_STOP | SERIAL_NONE_PARITY;
OUTB(pHWHead, pLCR, pHWHead->LCR);
DEBUGMSG (ZONE_OPEN,
(TEXT("SL_Open Setting DCB parameters\r\n")));
// Actually, for WENDY, FIFO always enabled....
// This sets up 256 byte trigger
// Shadow the FCR bitmask since reading this location is the IIR.
pHWHead->FCR = SERIAL_FCR_ENABLE | SERIAL_8_BYTE_HIGH_WATER ;
OUTB(pHWHead, pIIR_FCR,
(pHWHead->FCR | SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET) );
// Get defaults from the DCB structure
//SL_SetBaudRate( pHead, pHWHead->dcb.BaudRate );
//SL_SetByteSize( pHead, pHWHead->dcb.ByteSize );
//SL_SetStopBits( pHead, pHWHead->dcb.StopBits );
//SL_SetParity( pHead, pHWHead->dcb.Parity );
// Ugly. On PC's, the PIC is edge triggered. So because of the
// sequence in which we initialize interrupts, etc it is possible
// to have the UART generating its level based interrupt but the
// PIC never gets an edge, so we never start the entire interrupt/
// interrupt-acknowledge sequence. All we need to do here is ensure
// that any already pending interrupts get cleared at the UART.
pHWHead->IIR = INB(pHWHead, pIIR_FCR);
while( ! (pHWHead->IIR & 0x01) )
{
DEBUGMSG (ZONE_OPEN, (TEXT("!!IIR %X\r\n"), pHWHead->IIR));
// Reading LSR clears RLS interrupts.
ReadLSR( pHWHead );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -