📄 ns16550sio.c
字号:
/* ns16550Sio.c - NS 16550 UART tty driver */
/* Copyright 1984-2002 Wind River Systems, Inc. */
#include "copyright_wrs.h"
/*
modification history
--------------------
01m,24apr02,pmr SPR 75161: returning int from ns16550TxStartup() as required.
01l,03apr02,jnz fixed bug at ns16550SioDrvFuncs declraation.
01k,31oct01,mdg Corrected transmit interrtupt race condition found on
on MIPS Malta board with RM5261 processor. (SPR# 71830)
01j,14mar01,rcs corrected baud rate divisor calculation formula. (SPR# 63899)
01i,17sep97,dat fixed merge problems that caused baud rate setting to fail.
01h,06mar97,dat SPR 7899, max baud rate set to 115200.
01g,08may97,db added hardware options and modem control(SPRs #7570, #7082).
01f,18dec95,myz added case IIR_TIMEOUT in ns16550Int routine.
01e,28nov95,myz fixed bugs to work at 19200 baud or above with heavy traffic.
01d,09nov95,jdi doc: style cleanup.
01c,02nov95,myz undo 01b fix
01b,02nov95,p_m added test for 960CA and 960JX around access to lcr field
in order to compile on all architectures.
01a,24oct95,myz written from ns16550Serial.c.
*/
/*
DESCRIPTION
This is the driver for the NS16552 DUART. This device includes two universal
asynchronous receiver/transmitters, a baud rate generator, and a complete
modem control capability.
A NS16550_CHAN structure is used to describe the serial channel. This data
structure is defined in ns16550Sio.h.
Only asynchronous serial operation is supported by this driver.
The default serial settings are 8 data bits, 1 stop bit, no parity, 9600
baud, and software flow control.
USAGE
The BSP's sysHwInit() routine typically calls sysSerialHwInit(),
which creates the NS16550_CHAN structure and initializes all the values in the
structure (except the SIO_DRV_FUNCS) before calling ns16550DevInit().
The BSP's sysHwInit2() routine typically calls sysSerialHwInit2(), which
connects the chips interrupts via intConnect() (either the single
interrupt `ns16550Int' or the three interrupts `ns16550IntWr', `ns16550IntRd',
and `ns16550IntEx').
This driver handles setting of hardware options such as parity(odd, even) and
number of data bits(5, 6, 7, 8). Hardware flow control is provided with the
handshakes RTS/CTS. The function HUPCL(hang up on last close) is available.
When hardware flow control is enabled, the signals RTS and DTR are set TRUE
and remain set until a HUPCL is performed.
INCLUDE FILES: drv/sio/ns16552Sio.h
*/
/* includes */
#include "vxWorks.h"
#include "intLib.h"
#include "errnoLib.h"
#include "errno.h"
#include "sioLib.h"
#include "ns16552Sio.h"
/* local defines */
#ifndef SIO_HUP
# define SIO_OPEN 0x100A
# define SIO_HUP 0x100B
#endif
/* min/max baud rate */
#define NS16550_MIN_RATE 50
#define NS16550_MAX_RATE 115200
#define REG(reg, pchan) \
(*(volatile UINT8 *)((UINT32)pchan->regs + (reg * pchan->regDelta)))
#define REGPTR(reg, pchan) \
((volatile UINT8 *)((UINT32)pchan->regs + (reg * pchan->regDelta)))
/* static forward declarations */
LOCAL int ns16550CallbackInstall (SIO_CHAN *, int, STATUS (*)(), void *);
LOCAL STATUS ns16550DummyCallback ();
LOCAL void ns16550InitChannel (NS16550_CHAN *);
LOCAL STATUS ns16550BaudSet (NS16550_CHAN *, UINT);
LOCAL STATUS ns16550ModeSet (NS16550_CHAN *, UINT);
LOCAL STATUS ns16550Ioctl (NS16550_CHAN *, int, int);
LOCAL int ns16550TxStartup (NS16550_CHAN *);
LOCAL int ns16550PollOutput (NS16550_CHAN *, char);
LOCAL int ns16550PollInput (NS16550_CHAN *, char *);
LOCAL STATUS ns16550OptsSet (NS16550_CHAN *, UINT);
LOCAL STATUS ns16550Open (NS16550_CHAN * pChan );
LOCAL STATUS ns16550Hup (NS16550_CHAN * pChan );
/* driver functions */
static SIO_DRV_FUNCS ns16550SioDrvFuncs =
{
(int (*)())ns16550Ioctl,
(int (*)())ns16550TxStartup,
(int (*)())ns16550CallbackInstall,
(int (*)())ns16550PollInput,
(int (*)(SIO_CHAN *,char))ns16550PollOutput
};
/******************************************************************************
*
* ns16550DummyCallback - dummy callback routine.
*/
LOCAL STATUS ns16550DummyCallback (void)
{
return (ERROR);
}
/******************************************************************************
*
* ns16550DevInit - intialize an NS16550 channel
*
* This routine initializes some SIO_CHAN function pointers and then resets
* the chip in a quiescent state. Before this routine is called, the BSP
* must already have initialized all the device addresses, etc. in the
* NS16550_CHAN structure.
*
* RETURNS: N/A
*/
void ns16550DevInit
(
NS16550_CHAN * pChan /* pointer to channel */
)
{
int oldlevel = intLock ();
/* initialize the driver function pointers in the SIO_CHAN's */
pChan->pDrvFuncs = &ns16550SioDrvFuncs;
/* set the non BSP-specific constants */
pChan->getTxChar = ns16550DummyCallback;
pChan->putRcvChar = ns16550DummyCallback;
pChan->channelMode = 0; /* undefined */
pChan->options = (CLOCAL | CREAD | CS8);
pChan->mcr = MCR_OUT2;
/* reset the chip */
ns16550InitChannel (pChan);
intUnlock (oldlevel);
}
/*******************************************************************************
*
* ns16550InitChannel - initialize UART
*
* Initialize the number of data bits, parity and set the selected
* baud rate.
* Set the modem control signals if the option is selected.
*
* RETURNS: N/A
*/
LOCAL void ns16550InitChannel
(
NS16550_CHAN * pChan /* pointer to channel */
)
{
/* set the requested baud rate */
ns16550BaudSet(pChan, pChan->baudRate);
/* set the options */
ns16550OptsSet(pChan, pChan->options);
}
/*******************************************************************************
*
* ns16550OptsSet - set the serial options
*
* Set the channel operating mode to that specified. All sioLib options
* are supported: CLOCAL, HUPCL, CREAD, CSIZE, PARENB, and PARODD.
* When the HUPCL option is enabled, a connection is closed on the last
* close() call and opened on each open() call.
*
* Note, this routine disables the transmitter. The calling routine
* may have to re-enable it.
*
* RETURNS:
* Returns OK to indicate success, otherwise ERROR is returned
*/
LOCAL STATUS ns16550OptsSet
(
NS16550_CHAN * pChan, /* pointer to channel */
UINT options /* new hardware options */
)
{
FAST int oldlevel; /* current interrupt level mask */
pChan->lcr = 0;
pChan->mcr &= (~(MCR_RTS | MCR_DTR)); /* clear RTS and DTR bits */
if (pChan == NULL || options & 0xffffff00)
return ERROR;
switch (options & CSIZE)
{
case CS5:
pChan->lcr = CHAR_LEN_5; break;
case CS6:
pChan->lcr = CHAR_LEN_6; break;
case CS7:
pChan->lcr = CHAR_LEN_7; break;
default:
case CS8:
pChan->lcr = CHAR_LEN_8; break;
}
if (options & STOPB)
pChan->lcr |= LCR_STB;
else
pChan->lcr |= ONE_STOP;
switch (options & (PARENB | PARODD))
{
case PARENB|PARODD:
pChan->lcr |= LCR_PEN; break;
case PARENB:
pChan->lcr |= (LCR_PEN | LCR_EPS); break;
default:
case 0:
pChan->lcr |= PARITY_NONE; break;
}
REG(IER, pChan) = 0;
if (!(options & CLOCAL))
{
/* !clocal enables hardware flow control(DTR/DSR) */
pChan->mcr |= (MCR_DTR | MCR_RTS);
pChan->ier &= (~TxFIFO_BIT);
pChan->ier |= IER_EMSI; /* enable modem status interrupt */
}
else
pChan->ier &= ~IER_EMSI; /* disable modem status interrupt */
oldlevel = intLock ();
REG(LCR, pChan) = pChan->lcr;
REG(MCR, pChan) = pChan->mcr;
/* now reset the channel mode registers */
REG(FCR, pChan) = (RxCLEAR | TxCLEAR | FIFO_ENABLE);
if (options & CREAD)
pChan->ier |= RxFIFO_BIT;
if (pChan->channelMode == SIO_MODE_INT)
{
REG(IER, pChan) = pChan->ier;
}
intUnlock (oldlevel);
pChan->options = options;
return OK;
}
/*******************************************************************************
*
* ns16550Hup - hang up the modem control lines
*
* Resets the RTS and DTR signals and clears both the receiver and
* transmitter sections.
*
* RETURNS: OK
*/
LOCAL STATUS ns16550Hup
(
NS16550_CHAN * pChan /* pointer to channel */
)
{
FAST int oldlevel; /* current interrupt level mask */
oldlevel = intLock ();
pChan->mcr &= (~(MCR_RTS | MCR_DTR));
REG(MCR, pChan) = pChan->mcr;
REG(FCR, pChan) = (RxCLEAR | TxCLEAR);
intUnlock (oldlevel);
return (OK);
}
/*******************************************************************************
*
* ns16550Open - Set the modem control lines
*
* Set the modem control lines(RTS, DTR) TRUE if not already set.
* It also clears the receiver, transmitter and enables the fifo.
*
* RETURNS: OK
*/
LOCAL STATUS ns16550Open
(
NS16550_CHAN * pChan /* pointer to channel */
)
{
FAST int oldlevel; /* current interrupt level mask */
char mask;
mask = REG(MCR, pChan) & (MCR_RTS | MCR_DTR);
if (mask != (MCR_RTS | MCR_DTR))
{
/* RTS and DTR not set yet */
oldlevel = intLock ();
/* set RTS and DTR TRUE */
pChan->mcr |= (MCR_DTR | MCR_RTS);
REG(MCR, pChan) = pChan->mcr;
/* clear Tx and receive and enable FIFO */
REG(FCR, pChan) = (RxCLEAR | TxCLEAR | FIFO_ENABLE);
intUnlock (oldlevel);
}
return (OK);
}
/******************************************************************************
*
* ns16550BaudSet - change baud rate for channel
*
* This routine sets the baud rate for the UART. The interrupts are disabled
* during chip access.
*
* RETURNS: OK
*/
LOCAL STATUS ns16550BaudSet
(
NS16550_CHAN * pChan, /* pointer to channel */
UINT baud /* requested baud rate */
)
{
int oldlevel;
int divisor = ((pChan->xtal + (8 * baud)) / (16 * baud));
/* disable interrupts during chip access */
oldlevel = intLock ();
/* Enable access to the divisor latches by setting DLAB in LCR. */
REG(LCR, pChan) = LCR_DLAB | pChan->lcr;
/* Set divisor latches. */
REG(DLL,pChan) = divisor;
REG(DLM,pChan) = (divisor >> 8);
/* Restore line control register */
REG(LCR, pChan) = pChan->lcr;
pChan->baudRate = baud;
intUnlock (oldlevel);
return (OK);
}
/*******************************************************************************
*
* ns16550ModeSet - change channel mode setting
*
* This driver supports both polled and interrupt modes and is capable of
* switching between modes dynamically.
*
* If interrupt mode is desired this routine enables the channels receiver and
* transmitter interrupts. If the modem control option is TRUE, the Tx interrupt
* is disabled if the CTS signal is FALSE. It is enabled otherwise.
*
* If polled mode is desired the device interrupts are disabled.
*
* RETURNS:
* Returns a status of OK if the mode was set else ERROR.
*/
LOCAL STATUS ns16550ModeSet
(
NS16550_CHAN * pChan, /* pointer to channel */
UINT newMode /* mode requested */
)
{
FAST int oldlevel; /* current interrupt level mask */
char mask;
if ((newMode != SIO_MODE_POLL) && (newMode != SIO_MODE_INT))
return (ERROR);
oldlevel = intLock ();
if (newMode == SIO_MODE_INT)
{
/* Enable appropriate interrupts */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -