NetBSD/sys/dev/isa/com.c
cgd c821cb684a (1) conditionalize debugger usage in comeint with DDB, not COMCONSOLE;
latter would lead to undefined symbols if DDB not defined.
(2) check for break on console, and therefore debugger entry (if ddb
	in kernel) earlier, so that the device doesn't need to be open.
(3) return immediately after breaking into the debugger in comeint().
(4) only do the normal character input routine in comintr if receive
	mask yeilds _EXACLTY_ LSR_RXRDY.  if there's only a receive
	error, or there's a receive error _and_ a received character,
	do comeint().
(former two by me.  latter two from Bob Baron <rvb@cs.cmu.edu>.)
1995-05-12 17:54:41 +00:00

948 lines
21 KiB
C

/* $NetBSD: com.c,v 1.54 1995/05/12 17:54:41 cgd Exp $ */
/*-
* Copyright (c) 1993, 1994, 1995 Charles Hannum. All rights reserved.
* Copyright (c) 1991 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)com.c 7.5 (Berkeley) 5/16/91
*/
/*
* COM driver, based on HP dca driver
* uses National Semiconductor NS16450/NS16550AF UART
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/types.h>
#include <sys/device.h>
#include <machine/cpu.h>
#include <machine/pio.h>
#include <dev/isa/isavar.h>
#include <dev/isa/comreg.h>
#include <dev/ic/ns16550.h>
struct com_softc {
struct device sc_dev;
void *sc_ih;
struct tty *sc_tty;
int sc_overflows;
int sc_iobase;
u_char sc_hwflags;
#define COM_HW_NOIEN 0x01
#define COM_HW_FIFO 0x02
#define COM_HW_CONSOLE 0x40
u_char sc_swflags;
#define COM_SW_SOFTCAR 0x01
#define COM_SW_CLOCAL 0x02
#define COM_SW_CRTSCTS 0x04
#define COM_SW_MDMBUF 0x08
u_char sc_msr, sc_mcr;
};
int comprobe __P((struct device *, void *, void *));
void comattach __P((struct device *, struct device *, void *));
int comopen __P((dev_t, int, int, struct proc *));
int comclose __P((dev_t, int, int, struct proc *));
void comdiag __P((void *));
int comintr __P((void *));
int comparam __P((struct tty *, struct termios *));
void comstart __P((struct tty *));
struct cfdriver comcd = {
NULL, "com", comprobe, comattach, DV_TTY, sizeof(struct com_softc)
};
int comdefaultrate = TTYDEF_SPEED;
#ifdef COMCONSOLE
int comconsole = COMCONSOLE;
#else
int comconsole = -1;
#endif
int comconsinit;
int commajor;
#ifdef KGDB
#include <machine/remote-sl.h>
extern int kgdb_dev;
extern int kgdb_rate;
extern int kgdb_debug_init;
#endif
#define COMUNIT(x) (minor(x))
#define bis(c, b) do { const register int com_ad = (c); \
outb(com_ad, inb(com_ad) | (b)); } while(0)
#define bic(c, b) do { const register int com_ad = (c); \
outb(com_ad, inb(com_ad) & ~(b)); } while(0)
int
comspeed(speed)
long speed;
{
#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */
int x, err;
if (speed == 0)
return 0;
if (speed < 0)
return -1;
x = divrnd((COM_FREQ / 16), speed);
if (x <= 0)
return -1;
err = divrnd((COM_FREQ / 16) * 1000, speed * x) - 1000;
if (err < 0)
err = -err;
if (err > COM_TOLERANCE)
return -1;
return x;
#undef divrnd(n, q)
}
int
comprobe1(iobase)
int iobase;
{
/* force access to id reg */
outb(iobase + com_cfcr, 0);
outb(iobase + com_iir, 0);
if (inb(iobase + com_iir) & 0x38)
return 0;
return 1;
}
int
comprobe(parent, match, aux)
struct device *parent;
void *match, *aux;
{
struct isa_attach_args *ia = aux;
int iobase = ia->ia_iobase;
if (!comprobe1(iobase))
return 0;
ia->ia_iosize = COM_NPORTS;
ia->ia_msize = 0;
return 1;
}
void
comattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct com_softc *sc = (void *)self;
struct isa_attach_args *ia = aux;
struct cfdata *cf = sc->sc_dev.dv_cfdata;
int iobase = ia->ia_iobase;
struct tty *tp;
sc->sc_iobase = iobase;
sc->sc_hwflags = cf->cf_flags & COM_HW_NOIEN;
sc->sc_swflags = 0;
if (sc->sc_dev.dv_unit == comconsole)
delay(1000);
/* look for a NS 16550AF UART with FIFOs */
outb(iobase + com_fifo,
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14);
delay(100);
if ((inb(iobase + com_iir) & IIR_FIFO_MASK) == IIR_FIFO_MASK)
if ((inb(iobase + com_fifo) & FIFO_TRIGGER_14) == FIFO_TRIGGER_14) {
sc->sc_hwflags |= COM_HW_FIFO;
printf(": ns16550a, working fifo\n");
} else
printf(": ns82550 or ns16550, broken fifo\n");
else
printf(": ns82450 or ns16450, no fifo\n");
outb(iobase + com_fifo, 0);
/* disable interrupts */
outb(iobase + com_ier, 0);
outb(iobase + com_mcr, 0);
if (ia->ia_irq != IRQUNK)
sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_EDGE,
ISA_IPL_TTY, comintr, sc);
#ifdef KGDB
if (kgdb_dev == makedev(commajor, unit)) {
if (comconsole == unit)
kgdb_dev = -1; /* can't debug over console port */
else {
(void) cominit(unit, kgdb_rate);
if (kgdb_debug_init) {
/*
* Print prefix of device name,
* let kgdb_connect print the rest.
*/
printf("%s: ", sc->sc_dev.dv_xname);
kgdb_connect(1);
} else
printf("%s: kgdb enabled\n",
sc->sc_dev.dv_xname);
}
}
#endif
if (sc->sc_dev.dv_unit == comconsole) {
/*
* Need to reset baud rate, etc. of next print so reset
* comconsinit. Also make sure console is always "hardwired".
*/
comconsinit = 0;
sc->sc_hwflags |= COM_HW_CONSOLE;
sc->sc_swflags |= COM_SW_SOFTCAR;
}
}
int
comopen(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
int unit = COMUNIT(dev);
struct com_softc *sc;
int iobase;
struct tty *tp;
int s;
int error = 0;
if (unit >= comcd.cd_ndevs)
return ENXIO;
sc = comcd.cd_devs[unit];
if (!sc)
return ENXIO;
s = spltty();
if (!sc->sc_tty)
tp = sc->sc_tty = ttymalloc();
else
tp = sc->sc_tty;
tp->t_oproc = comstart;
tp->t_param = comparam;
tp->t_dev = dev;
if ((tp->t_state & TS_ISOPEN) == 0) {
tp->t_state |= TS_WOPEN;
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = TTYDEF_CFLAG;
if (sc->sc_swflags & COM_SW_CLOCAL)
tp->t_cflag |= CLOCAL;
if (sc->sc_swflags & COM_SW_CRTSCTS)
tp->t_cflag |= CRTSCTS;
if (sc->sc_swflags & COM_SW_MDMBUF)
tp->t_cflag |= MDMBUF;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = comdefaultrate;
comparam(tp, &tp->t_termios);
ttsetwater(tp);
iobase = sc->sc_iobase;
/* Set the FIFO threshold based on the receive speed. */
if (sc->sc_hwflags & COM_HW_FIFO)
outb(iobase + com_fifo,
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST |
(tp->t_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8));
/* flush any pending I/O */
(void) inb(iobase + com_lsr);
(void) inb(iobase + com_data);
/* you turn me on, baby */
sc->sc_mcr = MCR_DTR | MCR_RTS;
if (!(sc->sc_hwflags & COM_HW_NOIEN))
sc->sc_mcr |= MCR_IENABLE;
outb(iobase + com_mcr, sc->sc_mcr);
outb(iobase + com_ier,
IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
sc->sc_msr = inb(iobase + com_msr);
if (sc->sc_swflags & COM_SW_SOFTCAR || sc->sc_msr & MSR_DCD ||
tp->t_cflag & MDMBUF)
tp->t_state |= TS_CARR_ON;
else
tp->t_state &= ~TS_CARR_ON;
} else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) {
splx(s);
return EBUSY;
}
/* wait for carrier if necessary */
if ((flag & O_NONBLOCK) == 0)
while ((tp->t_cflag & CLOCAL) == 0 &&
(tp->t_state & TS_CARR_ON) == 0) {
tp->t_state |= TS_WOPEN;
error = ttysleep(tp, (caddr_t)&tp->t_rawq,
TTIPRI | PCATCH, ttopen, 0);
if (error) {
/* XXX should turn off chip if we're the
only waiter */
splx(s);
return error;
}
}
splx(s);
return (*linesw[tp->t_line].l_open)(dev, tp);
}
int
comclose(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
int unit = COMUNIT(dev);
struct com_softc *sc = comcd.cd_devs[unit];
struct tty *tp = sc->sc_tty;
int iobase = sc->sc_iobase;
(*linesw[tp->t_line].l_close)(tp, flag);
#ifdef KGDB
/* do not disable interrupts if debugging */
if (kgdb_dev != makedev(commajor, unit))
#endif
{
bic(iobase + com_cfcr, CFCR_SBREAK);
outb(iobase + com_ier, 0);
if (tp->t_cflag & HUPCL &&
(sc->sc_swflags & COM_SW_SOFTCAR) == 0)
/* XXX perhaps only clear DTR */
outb(iobase + com_mcr, 0);
}
ttyclose(tp);
#ifdef notyet /* XXXX */
if (unit != comconsole) {
ttyfree(tp);
sc->sc_tty = 0;
}
#endif
return 0;
}
int
comread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct com_softc *sc = comcd.cd_devs[COMUNIT(dev)];
struct tty *tp = sc->sc_tty;
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
int
comwrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct com_softc *sc = comcd.cd_devs[COMUNIT(dev)];
struct tty *tp = sc->sc_tty;
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
struct tty *
comtty(dev)
dev_t dev;
{
struct com_softc *sc = comcd.cd_devs[COMUNIT(dev)];
struct tty *tp = sc->sc_tty;
return (tp);
}
static u_char
tiocm_xxx2mcr(data)
int data;
{
u_char m = 0;
if (data & TIOCM_DTR)
m |= MCR_DTR;
if (data & TIOCM_RTS)
m |= MCR_RTS;
return m;
}
int
comioctl(dev, cmd, data, flag, p)
dev_t dev;
u_long cmd;
caddr_t data;
int flag;
struct proc *p;
{
int unit = COMUNIT(dev);
struct com_softc *sc = comcd.cd_devs[unit];
struct tty *tp = sc->sc_tty;
int iobase = sc->sc_iobase;
int error;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return error;
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return error;
switch (cmd) {
case TIOCSBRK:
bis(iobase + com_cfcr, CFCR_SBREAK);
break;
case TIOCCBRK:
bic(iobase + com_cfcr, CFCR_SBREAK);
break;
case TIOCSDTR:
outb(iobase + com_mcr, sc->sc_mcr |= (MCR_DTR | MCR_RTS));
break;
case TIOCCDTR:
outb(iobase + com_mcr, sc->sc_mcr &= ~(MCR_DTR | MCR_RTS));
break;
case TIOCMSET:
sc->sc_mcr &= ~(MCR_DTR | MCR_RTS);
case TIOCMBIS:
outb(iobase + com_mcr,
sc->sc_mcr |= tiocm_xxx2mcr(*(int *)data));
break;
case TIOCMBIC:
outb(iobase + com_mcr,
sc->sc_mcr &= ~tiocm_xxx2mcr(*(int *)data));
break;
case TIOCMGET: {
u_char m;
int bits = 0;
m = sc->sc_mcr;
if (m & MCR_DTR)
bits |= TIOCM_DTR;
if (m & MCR_RTS)
bits |= TIOCM_RTS;
m = sc->sc_msr;
if (m & MSR_DCD)
bits |= TIOCM_CD;
if (m & MSR_CTS)
bits |= TIOCM_CTS;
if (m & MSR_DSR)
bits |= TIOCM_DSR;
if (m & (MSR_RI | MSR_TERI))
bits |= TIOCM_RI;
if (inb(iobase + com_ier))
bits |= TIOCM_LE;
*(int *)data = bits;
break;
}
case TIOCGFLAGS: {
int bits = 0;
if (sc->sc_swflags & COM_SW_SOFTCAR)
bits |= TIOCFLAG_SOFTCAR;
if (sc->sc_swflags & COM_SW_CLOCAL)
bits |= TIOCFLAG_CLOCAL;
if (sc->sc_swflags & COM_SW_CRTSCTS)
bits |= TIOCFLAG_CRTSCTS;
if (sc->sc_swflags & COM_SW_MDMBUF)
bits |= TIOCFLAG_MDMBUF;
*(int *)data = bits;
break;
}
case TIOCSFLAGS: {
int userbits, driverbits = 0;
error = suser(p->p_ucred, &p->p_acflag);
if (error != 0)
return(EPERM);
userbits = *(int *)data;
if ((userbits & TIOCFLAG_SOFTCAR) ||
(sc->sc_hwflags & COM_HW_CONSOLE))
driverbits |= COM_SW_SOFTCAR;
if (userbits & TIOCFLAG_CLOCAL)
driverbits |= COM_SW_CLOCAL;
if (userbits & TIOCFLAG_CRTSCTS)
driverbits |= COM_SW_CRTSCTS;
if (userbits & TIOCFLAG_MDMBUF)
driverbits |= COM_SW_MDMBUF;
sc->sc_swflags = driverbits;
break;
}
default:
return ENOTTY;
}
return 0;
}
int
comparam(tp, t)
struct tty *tp;
struct termios *t;
{
struct com_softc *sc = comcd.cd_devs[COMUNIT(tp->t_dev)];
int iobase = sc->sc_iobase;
int ospeed = comspeed(t->c_ospeed);
u_char cfcr;
int s;
/* check requested parameters */
if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
return EINVAL;
switch (t->c_cflag & CSIZE) {
case CS5:
cfcr = CFCR_5BITS;
break;
case CS6:
cfcr = CFCR_6BITS;
break;
case CS7:
cfcr = CFCR_7BITS;
break;
case CS8:
cfcr = CFCR_8BITS;
break;
}
if (t->c_cflag & PARENB) {
cfcr |= CFCR_PENAB;
if ((t->c_cflag & PARODD) == 0)
cfcr |= CFCR_PEVEN;
}
if (t->c_cflag & CSTOPB)
cfcr |= CFCR_STOPB;
s = spltty();
if (ospeed == 0)
outb(iobase + com_mcr, sc->sc_mcr &= ~MCR_DTR);
/*
* Set the FIFO threshold based on the receive speed, if we are
* changing it.
*
* XXX
* It would be better if we waited for the FIFO to empty, so we don't
* lose any in-transit characters.
*/
if (tp->t_ispeed != t->c_ispeed) {
if (sc->sc_hwflags & COM_HW_FIFO)
outb(iobase + com_fifo,
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST |
(t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8));
}
outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
outb(iobase + com_dlbl, ospeed);
outb(iobase + com_dlbh, ospeed>>8);
outb(iobase + com_cfcr, cfcr);
if (ospeed != 0)
outb(iobase + com_mcr, sc->sc_mcr |= MCR_DTR);
/* When not using CRTSCTS, RTS follows DTR. */
if ((t->c_cflag & CRTSCTS) == 0) {
if (sc->sc_mcr & MCR_DTR) {
if ((sc->sc_mcr & MCR_RTS) == 0)
outb(iobase + com_mcr, sc->sc_mcr |= MCR_RTS);
} else {
if (sc->sc_mcr & MCR_RTS)
outb(iobase + com_mcr, sc->sc_mcr &= ~MCR_RTS);
}
}
/*
* If CTS is off and CRTSCTS is changed, we must toggle TS_TTSTOP.
* XXX should be done at tty layer.
*/
if ((sc->sc_msr & MSR_CTS) == 0 &&
(tp->t_cflag & CRTSCTS) != (t->c_cflag & CRTSCTS)) {
if ((t->c_cflag & CRTSCTS) == 0) {
tp->t_state &= ~TS_TTSTOP;
(*linesw[tp->t_line].l_start)(tp);
} else
tp->t_state |= TS_TTSTOP;
}
/*
* If DCD is off and MDMBUF is changed, we must toggle TS_TTSTOP.
* XXX should be done at tty layer.
*/
if ((sc->sc_swflags & COM_SW_SOFTCAR) == 0 &&
(sc->sc_msr & MSR_DCD) == 0 &&
(tp->t_cflag & MDMBUF) != (t->c_cflag & MDMBUF)) {
if ((t->c_cflag & MDMBUF) == 0) {
tp->t_state &= ~TS_TTSTOP;
(*linesw[tp->t_line].l_start)(tp);
} else
tp->t_state |= TS_TTSTOP;
}
/* and copy to tty */
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = t->c_cflag;
splx(s);
return 0;
}
void
comstart(tp)
struct tty *tp;
{
struct com_softc *sc = comcd.cd_devs[COMUNIT(tp->t_dev)];
int iobase = sc->sc_iobase;
int s;
s = spltty();
if (tp->t_state & (TS_TTSTOP | TS_BUSY))
goto out;
#if 0 /* XXXX I think this is handled adequately by commint() and comparam(). */
if (tp->t_cflag & CRTSCTS && (sc->sc_mcr & MSR_CTS) == 0)
goto out;
#endif
if (tp->t_outq.c_cc <= tp->t_lowat) {
if (tp->t_state & TS_ASLEEP) {
tp->t_state &= ~TS_ASLEEP;
wakeup((caddr_t)&tp->t_outq);
}
selwakeup(&tp->t_wsel);
}
if (tp->t_outq.c_cc == 0)
goto out;
tp->t_state |= TS_BUSY;
if ((inb(iobase + com_lsr) & LSR_TXRDY) == 0)
goto out;
if (sc->sc_hwflags & COM_HW_FIFO) {
u_char buffer[16], *cp = buffer;
int n = q_to_b(&tp->t_outq, cp, sizeof buffer);
do {
outb(iobase + com_data, *cp++);
} while (--n);
} else
outb(iobase + com_data, getc(&tp->t_outq));
out:
splx(s);
}
/*
* Stop output on a line.
*/
void
comstop(tp, flag)
struct tty *tp;
{
int s;
s = spltty();
if (tp->t_state & TS_BUSY)
if ((tp->t_state & TS_TTSTOP) == 0)
tp->t_state |= TS_FLUSH;
splx(s);
}
static inline void
comeint(sc, stat)
struct com_softc *sc;
int stat;
{
struct tty *tp = sc->sc_tty;
int iobase = sc->sc_iobase;
int c;
c = inb(iobase + com_data);
#ifdef DDB
if ((stat & LSR_BI) && (sc->sc_dev.dv_unit == comconsole)) {
Debugger();
return;
}
#endif
if ((tp->t_state & TS_ISOPEN) == 0) {
#ifdef KGDB
/* we don't care about parity errors */
if (((stat & (LSR_BI | LSR_FE | LSR_PE)) == LSR_PE) &&
kgdb_dev == makedev(commajor, unit) && c == FRAME_END)
kgdb_connect(0); /* trap into kgdb */
#endif
return;
}
if (stat & (LSR_BI | LSR_FE))
c |= TTY_FE;
else if (stat & LSR_PE)
c |= TTY_PE;
if (stat & LSR_OE) {
if (sc->sc_overflows++ == 0)
timeout(comdiag, sc, 60 * hz);
}
/* XXXX put in FIFO and process later */
(*linesw[tp->t_line].l_rint)(c, tp);
}
static inline void
commint(sc)
struct com_softc *sc;
{
struct tty *tp = sc->sc_tty;
int iobase = sc->sc_iobase;
u_char msr, delta;
msr = inb(iobase + com_msr);
delta = msr ^ sc->sc_msr;
sc->sc_msr = msr;
if (delta & MSR_DCD && (sc->sc_swflags & COM_SW_SOFTCAR) == 0) {
if (msr & MSR_DCD)
(void)(*linesw[tp->t_line].l_modem)(tp, 1);
else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0)
outb(iobase + com_mcr,
sc->sc_mcr &= ~(MCR_DTR | MCR_RTS));
}
if (delta & MSR_CTS && tp->t_cflag & CRTSCTS) {
/* the line is up and we want to do rts/cts flow control */
if (msr & MSR_CTS) {
tp->t_state &= ~TS_TTSTOP;
(*linesw[tp->t_line].l_start)(tp);
} else
tp->t_state |= TS_TTSTOP;
}
}
void
comdiag(arg)
void *arg;
{
struct com_softc *sc = arg;
int overflows;
int s;
s = spltty();
overflows = sc->sc_overflows;
sc->sc_overflows = 0;
splx(s);
if (overflows)
log(LOG_WARNING, "%s: %d silo overflow%s\n",
sc->sc_dev.dv_xname, overflows, overflows == 1 ? "" : "s");
}
int
comintr(arg)
void *arg;
{
struct com_softc *sc = arg;
int iobase = sc->sc_iobase;
struct tty *tp;
u_char code;
code = inb(iobase + com_iir) & IIR_IMASK;
if (code & IIR_NOPEND)
return 0;
for (;;) {
if (code & IIR_RXRDY) {
tp = sc->sc_tty;
/* XXXX put in FIFO and process later */
while (code = (inb(iobase + com_lsr) & LSR_RCV_MASK)) {
if (code == LSR_RXRDY) {
code = inb(iobase + com_data);
if (tp->t_state & TS_ISOPEN)
(*linesw[tp->t_line].l_rint)(code, tp);
#ifdef KGDB
else {
if (kgdb_dev == makedev(commajor, unit) &&
code == FRAME_END)
kgdb_connect(0);
}
#endif
} else
comeint(sc, code);
}
} else if (code == IIR_TXRDY) {
tp = sc->sc_tty;
tp->t_state &= ~TS_BUSY;
if (tp->t_state & TS_FLUSH)
tp->t_state &= ~TS_FLUSH;
else
if (tp->t_line)
(*linesw[tp->t_line].l_start)(tp);
else
comstart(tp);
} else if (code == IIR_MLSC) {
commint(sc);
} else {
log(LOG_WARNING, "%s: weird interrupt: iir=0x%02x\n",
sc->sc_dev.dv_xname, code);
}
code = inb(iobase + com_iir) & IIR_IMASK;
if (code & IIR_NOPEND)
return 1;
}
}
/*
* Following are all routines needed for COM to act as console
*/
#include <dev/cons.h>
void
comcnprobe(cp)
struct consdev *cp;
{
if (!comprobe1(CONADDR)) {
cp->cn_pri = CN_DEAD;
return;
}
/* locate the major number */
for (commajor = 0; commajor < nchrdev; commajor++)
if (cdevsw[commajor].d_open == comopen)
break;
/* initialize required fields */
cp->cn_dev = makedev(commajor, CONUNIT);
#ifdef COMCONSOLE
cp->cn_pri = CN_REMOTE; /* Force a serial port console */
#else
cp->cn_pri = CN_NORMAL;
#endif
}
void
comcninit(cp)
struct consdev *cp;
{
cominit(CONUNIT, comdefaultrate);
comconsole = CONUNIT;
comconsinit = 0;
}
cominit(unit, rate)
int unit, rate;
{
int s = splhigh();
int iobase = CONADDR;
u_char stat;
outb(iobase + com_cfcr, CFCR_DLAB);
rate = comspeed(comdefaultrate);
outb(iobase + com_dlbl, rate);
outb(iobase + com_dlbh, rate >> 8);
outb(iobase + com_cfcr, CFCR_8BITS);
outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY);
outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_4);
stat = inb(iobase + com_iir);
splx(s);
}
comcngetc(dev)
dev_t dev;
{
int s = splhigh();
int iobase = CONADDR;
u_char stat, c;
while (((stat = inb(iobase + com_lsr)) & LSR_RXRDY) == 0)
;
c = inb(iobase + com_data);
stat = inb(iobase + com_iir);
splx(s);
return c;
}
/*
* Console kernel output character routine.
*/
void
comcnputc(dev, c)
dev_t dev;
int c;
{
int s = splhigh();
int iobase = CONADDR;
u_char stat;
register int timo;
#ifdef KGDB
if (dev != kgdb_dev)
#endif
if (comconsinit == 0) {
(void) cominit(COMUNIT(dev), comdefaultrate);
comconsinit = 1;
}
/* wait for any pending transmission to finish */
timo = 50000;
while (((stat = inb(iobase + com_lsr)) & LSR_TXRDY) == 0 && --timo)
;
outb(iobase + com_data, c);
/* wait for this transmission to complete */
timo = 1500000;
while (((stat = inb(iobase + com_lsr)) & LSR_TXRDY) == 0 && --timo)
;
/* clear any interrupts generated by this transmission */
stat = inb(iobase + com_iir);
splx(s);
}
void
comcnpollc(dev, on)
dev_t dev;
int on;
{
}