freebsd-dev/sys/dev/rc/rc.c
John Baldwin 844933a831 Move an optimization check so that it breaks out of the correct loop.
Basically, as soon as we have run out of events to handle in the swi,
we stop looping through all the channels in a for loop.
2003-02-26 19:21:56 +00:00

1598 lines
41 KiB
C

/*
* Copyright (C) 1995 by Pavel Antonov, Moscow, Russia.
* Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia.
* Copyright (C) 2002 by John Baldwin <jhb@FreeBSD.org>
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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.
*
* $FreeBSD$
*/
/*
* SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver
*
*/
/*#define RCDEBUG*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/interrupt.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <dev/ic/cd180.h>
#include <dev/rc/rcreg.h>
#include <isa/isavar.h>
#define IOBASE_ADDRS 14
#define DEV_TO_RC(dev) (struct rc_chans *)((dev)->si_drv1)
#define TTY_TO_RC(tty) DEV_TO_RC((tty)->t_dev)
#define rcin(sc, port) RC_IN(sc, port)
#define rcout(sc, port, v) RC_OUT(sc, port, v)
#define WAITFORCCR(sc, chan) rc_wait0((sc), (chan), __LINE__)
#define CCRCMD(sc, chan, cmd) do { \
WAITFORCCR((sc), (chan)); \
rcout((sc), CD180_CCR, (cmd)); \
} while (0)
#define RC_IBUFSIZE 256
#define RB_I_HIGH_WATER (TTYHOG - 2 * RC_IBUFSIZE)
#define RC_OBUFSIZE 512
#define RC_IHIGHWATER (3 * RC_IBUFSIZE / 4)
#define INPUT_FLAGS_SHIFT (2 * RC_IBUFSIZE)
#define LOTS_OF_EVENTS 64
#define RC_FAKEID 0x10
#define CALLOUT(dev) (((intptr_t)(dev)->si_drv2) != 0)
/* Per-channel structure */
struct rc_chans {
struct rc_softc *rc_rcb; /* back ptr */
dev_t rc_dev; /* non-callout device */
dev_t rc_cdev; /* callout device */
u_short rc_flags; /* Misc. flags */
int rc_chan; /* Channel # */
u_char rc_ier; /* intr. enable reg */
u_char rc_msvr; /* modem sig. status */
u_char rc_cor2; /* options reg */
u_char rc_pendcmd; /* special cmd pending */
u_int rc_dtrwait; /* dtr timeout */
u_int rc_dcdwaits; /* how many waits DCD in open */
u_char rc_hotchar; /* end packed optimize */
struct tty rc_tp; /* tty struct */
u_char *rc_iptr; /* Chars input buffer */
u_char *rc_hiwat; /* hi-water mark */
u_char *rc_bufend; /* end of buffer */
u_char *rc_optr; /* ptr in output buf */
u_char *rc_obufend; /* end of output buf */
u_char rc_ibuf[4 * RC_IBUFSIZE]; /* input buffer */
u_char rc_obuf[RC_OBUFSIZE]; /* output buffer */
struct callout rc_dtrcallout;
};
/* Per-board structure */
struct rc_softc {
device_t sc_dev;
struct resource *sc_irq;
struct resource *sc_port[IOBASE_ADDRS];
int sc_irqrid;
void *sc_hwicookie;
bus_space_tag_t sc_bt;
bus_space_handle_t sc_bh;
u_int sc_unit; /* unit # */
u_char sc_dtr; /* DTR status */
int sc_opencount;
int sc_scheduled_event;
void *sc_swicookie;
struct rc_chans sc_channels[CD180_NCHAN]; /* channels */
};
/* Static prototypes */
static void rc_release_resources(device_t dev);
static void rc_intr(void *);
static void rc_hwreset(struct rc_softc *, unsigned int);
static int rc_test(struct rc_softc *);
static void rc_discard_output(struct rc_chans *);
static void rc_hardclose(struct rc_chans *);
static int rc_modctl(struct rc_chans *, int, int);
static void rc_start(struct tty *);
static void rc_stop(struct tty *, int rw);
static int rc_param(struct tty *, struct termios *);
static void rc_pollcard(void *);
static void rc_reinit(struct rc_softc *);
#ifdef RCDEBUG
static void printrcflags();
#endif
static void rc_dtrwakeup(void *);
static void disc_optim(struct tty *tp, struct termios *t, struct rc_chans *);
static void rc_wait0(struct rc_softc *sc, int chan, int line);
static d_open_t rcopen;
static d_close_t rcclose;
static d_ioctl_t rcioctl;
#define CDEV_MAJOR 63
static struct cdevsw rc_cdevsw = {
/* open */ rcopen,
/* close */ rcclose,
/* read */ ttyread,
/* write */ ttywrite,
/* ioctl */ rcioctl,
/* poll */ ttypoll,
/* mmap */ nommap,
/* strategy */ nostrategy,
/* name */ "rc",
/* maj */ CDEV_MAJOR,
/* dump */ nodump,
/* psize */ nopsize,
/* flags */ D_TTY | D_KQFILTER,
/* kqfilter */ ttykqfilter,
};
static devclass_t rc_devclass;
/* Flags */
#define RC_DTR_OFF 0x0001 /* DTR wait, for close/open */
#define RC_ACTOUT 0x0002 /* Dial-out port active */
#define RC_RTSFLOW 0x0004 /* RTS flow ctl enabled */
#define RC_CTSFLOW 0x0008 /* CTS flow ctl enabled */
#define RC_DORXFER 0x0010 /* RXFER event planned */
#define RC_DOXXFER 0x0020 /* XXFER event planned */
#define RC_MODCHG 0x0040 /* Modem status changed */
#define RC_OSUSP 0x0080 /* Output suspended */
#define RC_OSBUSY 0x0100 /* start() routine in progress */
#define RC_WAS_BUFOVFL 0x0200 /* low-level buffer ovferflow */
#define RC_WAS_SILOVFL 0x0400 /* silo buffer overflow */
#define RC_SEND_RDY 0x0800 /* ready to send */
/* Table for translation of RCSR status bits to internal form */
static int rc_rcsrt[16] = {
0, TTY_OE, TTY_FE,
TTY_FE|TTY_OE, TTY_PE, TTY_PE|TTY_OE,
TTY_PE|TTY_FE, TTY_PE|TTY_FE|TTY_OE, TTY_BI,
TTY_BI|TTY_OE, TTY_BI|TTY_FE, TTY_BI|TTY_FE|TTY_OE,
TTY_BI|TTY_PE, TTY_BI|TTY_PE|TTY_OE, TTY_BI|TTY_PE|TTY_FE,
TTY_BI|TTY_PE|TTY_FE|TTY_OE
};
static int rc_ports[] =
{ 0x220, 0x240, 0x250, 0x260, 0x2a0, 0x2b0, 0x300, 0x320 };
static int iobase_addrs[IOBASE_ADDRS] =
{ 0, 0x400, 0x800, 0xc00, 0x1400, 0x1800, 0x1c00, 0x2000,
0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x8000 };
/**********************************************/
static int
rc_probe(device_t dev)
{
u_int port;
int i, found;
/*
* We don't know of any PnP ID's for these cards.
*/
if (isa_get_logicalid(dev) != 0)
return (ENXIO);
/*
* We have to have an IO port hint that is valid.
*/
port = isa_get_port(dev);
if (port == -1)
return (ENXIO);
found = 0;
for (i = 0; i < sizeof(rc_ports) / sizeof(int); i++)
if (rc_ports[i] == port) {
found = 1;
break;
}
if (!found)
return (ENXIO);
/*
* We have to have an IRQ hint.
*/
if (isa_get_irq(dev) == -1)
return (ENXIO);
device_set_desc(dev, "SDL Riscom/8");
return (0);
}
static int
rc_attach(device_t dev)
{
struct rc_chans *rc;
struct tty *tp;
struct rc_softc *sc;
u_int port;
int base, chan, error, i, x;
dev_t cdev;
sc = device_get_softc(dev);
sc->sc_dev = dev;
/*
* We need to have IO ports. Lots of them. We need
* the following ranges relative to the base port:
* 0x0 - 0x10
* 0x400 - 0x410
* 0x800 - 0x810
* 0xc00 - 0xc10
* 0x1400 - 0x1410
* 0x1800 - 0x1810
* 0x1c00 - 0x1c10
* 0x2000 - 0x2010
* 0x3000 - 0x3010
* 0x3400 - 0x3410
* 0x3800 - 0x3810
* 0x3c00 - 0x3c10
* 0x4000 - 0x4010
* 0x8000 - 0x8010
*/
port = isa_get_port(dev);
for (i = 0; i < IOBASE_ADDRS; i++)
if (bus_set_resource(dev, SYS_RES_IOPORT, i,
port + iobase_addrs[i], 0x10) != 0)
return (ENXIO);
error = ENOMEM;
for (i = 0; i < IOBASE_ADDRS; i++) {
x = i;
sc->sc_port[i] = bus_alloc_resource(dev, SYS_RES_IOPORT, &x,
0ul, ~0ul, 0x10, RF_ACTIVE);
if (x != i) {
device_printf(dev, "ioport %d was rid %d\n", i, x);
goto fail;
}
if (sc->sc_port[i] == NULL) {
device_printf(dev, "failed to alloc ioports %x-%x\n",
port + iobase_addrs[i],
port + iobase_addrs[i] + 0x10);
goto fail;
}
}
sc->sc_bt = rman_get_bustag(sc->sc_port[0]);
sc->sc_bh = rman_get_bushandle(sc->sc_port[0]);
sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_irqrid,
0ul, ~0ul, 1, RF_ACTIVE);
if (sc->sc_irq == NULL) {
device_printf(dev, "failed to alloc IRQ\n");
goto fail;
}
/*
* Now do some actual tests to make sure it works.
*/
error = ENXIO;
rcout(sc, CD180_PPRL, 0x22); /* Random values to Prescale reg. */
rcout(sc, CD180_PPRH, 0x11);
if (rcin(sc, CD180_PPRL) != 0x22 || rcin(sc, CD180_PPRH) != 0x11)
goto fail;
if (rc_test(sc))
goto fail;
/*
* Ok, start actually hooking things up.
*/
sc->sc_unit = device_get_unit(dev);
/*sc->sc_chipid = 0x10 + device_get_unit(dev);*/
device_printf(dev, "%d chans, firmware rev. %c\n",
CD180_NCHAN, (rcin(sc, CD180_GFRCR) & 0xF) + 'A');
rc = sc->sc_channels;
base = CD180_NCHAN * sc->sc_unit;
for (chan = 0; chan < CD180_NCHAN; chan++, rc++) {
rc->rc_rcb = sc;
rc->rc_chan = chan;
rc->rc_iptr = rc->rc_ibuf;
rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE];
rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER];
rc->rc_optr = rc->rc_obufend = rc->rc_obuf;
rc->rc_dtrwait = 3 * hz;
callout_init(&rc->rc_dtrcallout, 0);
tp = &rc->rc_tp;
ttychars(tp);
tp->t_lflag = tp->t_iflag = tp->t_oflag = 0;
tp->t_cflag = TTYDEF_CFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
cdev = make_dev(&rc_cdevsw, chan + base,
UID_ROOT, GID_WHEEL, 0600, "ttym%d", chan + base);
cdev->si_drv1 = rc;
cdev->si_drv2 = 0;
cdev->si_tty = tp;
rc->rc_dev = cdev;
cdev = make_dev(&rc_cdevsw, chan + base + 128,
UID_UUCP, GID_DIALER, 0660, "cuam%d", chan + base);
cdev->si_drv1 = rc;
cdev->si_drv2 = (void *)1;
cdev->si_tty = tp;
rc->rc_cdev = cdev;
}
error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_TTY, rc_intr, sc,
&sc->sc_hwicookie);
if (error) {
device_printf(dev, "failed to register interrupt handler\n");
goto fail;
}
swi_add(&tty_ithd, "tty:rc", rc_pollcard, sc, SWI_TTY, 0,
&sc->sc_swicookie);
return (0);
fail:
rc_release_resources(dev);
return (error);
}
static int
rc_detach(device_t dev)
{
struct rc_softc *sc;
struct rc_chans *rc;
int error, i, s;
sc = device_get_softc(dev);
if (sc->sc_opencount > 0)
return (EBUSY);
sc->sc_opencount = -1;
rc = sc->sc_channels;
for (i = 0; i < CD180_NCHAN; i++, rc++) {
destroy_dev(rc->rc_dev);
destroy_dev(rc->rc_cdev);
}
rc = sc->sc_channels;
s = splsoftclock();
for (i = 0; i < CD180_NCHAN; i++) {
if ((rc->rc_flags & RC_DTR_OFF) &&
!callout_stop(&rc->rc_dtrcallout))
tsleep(&rc->rc_dtrwait, TTIPRI, "rcdtrdet", 0);
}
error = bus_teardown_intr(dev, sc->sc_irq, sc->sc_hwicookie);
if (error)
device_printf(dev, "failed to deregister interrupt handler\n");
ithread_remove_handler(sc->sc_swicookie);
rc_release_resources(dev);
return (0);
}
static void
rc_release_resources(device_t dev)
{
struct rc_softc *sc;
int i;
sc = device_get_softc(dev);
if (sc->sc_irq != NULL) {
bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
sc->sc_irq);
sc->sc_irq = NULL;
}
for (i = 0; i < IOBASE_ADDRS; i++) {
if (sc->sc_port[i] == NULL)
break;
bus_release_resource(dev, SYS_RES_IOPORT, i, sc->sc_port[i]);
sc->sc_port[i] = NULL;
}
}
/* RC interrupt handling */
static void
rc_intr(void *arg)
{
struct rc_softc *sc;
struct rc_chans *rc;
int resid, chan;
u_char val, iack, bsr, ucnt, *optr;
int good_data, t_state;
sc = (struct rc_softc *)arg;
bsr = ~(rcin(sc, RC_BSR));
if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) {
device_printf(sc->sc_dev, "extra interrupt\n");
rcout(sc, CD180_EOIR, 0);
return;
}
while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) {
#ifdef RCDEBUG_DETAILED
device_printf(sc->sc_dev, "intr (%p) %s%s%s%s\n", arg, bsr,
(bsr & RC_BSR_TOUT)?"TOUT ":"",
(bsr & RC_BSR_RXINT)?"RXINT ":"",
(bsr & RC_BSR_TXINT)?"TXINT ":"",
(bsr & RC_BSR_MOINT)?"MOINT":"");
#endif
if (bsr & RC_BSR_TOUT) {
device_printf(sc->sc_dev,
"hardware failure, reset board\n");
rcout(sc, RC_CTOUT, 0);
rc_reinit(sc);
return;
}
if (bsr & RC_BSR_RXINT) {
iack = rcin(sc, RC_PILR_RX);
good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID));
if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) {
device_printf(sc->sc_dev,
"fake rxint: %02x\n", iack);
goto more_intrs;
}
chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
rc = &sc->sc_channels[chan];
t_state = rc->rc_tp.t_state;
/* Do RTS flow control stuff */
if ( (rc->rc_flags & RC_RTSFLOW)
|| !(t_state & TS_ISOPEN)
) {
if ( ( !(t_state & TS_ISOPEN)
|| (t_state & TS_TBLOCK)
)
&& (rc->rc_msvr & MSVR_RTS)
)
rcout(sc, CD180_MSVR,
rc->rc_msvr &= ~MSVR_RTS);
else if (!(rc->rc_msvr & MSVR_RTS))
rcout(sc, CD180_MSVR,
rc->rc_msvr |= MSVR_RTS);
}
ucnt = rcin(sc, CD180_RDCR) & 0xF;
resid = 0;
if (t_state & TS_ISOPEN) {
/* check for input buffer overflow */
if ((rc->rc_iptr + ucnt) >= rc->rc_bufend) {
resid = ucnt;
ucnt = rc->rc_bufend - rc->rc_iptr;
resid -= ucnt;
if (!(rc->rc_flags & RC_WAS_BUFOVFL)) {
rc->rc_flags |= RC_WAS_BUFOVFL;
sc->sc_scheduled_event++;
}
}
optr = rc->rc_iptr;
/* check foor good data */
if (good_data) {
while (ucnt-- > 0) {
val = rcin(sc, CD180_RDR);
optr[0] = val;
optr[INPUT_FLAGS_SHIFT] = 0;
optr++;
sc->sc_scheduled_event++;
if (val != 0 && val == rc->rc_hotchar)
swi_sched(sc->sc_swicookie, 0);
}
} else {
/* Store also status data */
while (ucnt-- > 0) {
iack = rcin(sc, CD180_RCSR);
if (iack & RCSR_Timeout)
break;
if ( (iack & RCSR_OE)
&& !(rc->rc_flags & RC_WAS_SILOVFL)) {
rc->rc_flags |= RC_WAS_SILOVFL;
sc->sc_scheduled_event++;
}
val = rcin(sc, CD180_RDR);
/*
Don't store PE if IGNPAR and BREAK if IGNBRK,
this hack allows "raw" tty optimization
works even if IGN* is set.
*/
if ( !(iack & (RCSR_PE|RCSR_FE|RCSR_Break))
|| ((!(iack & (RCSR_PE|RCSR_FE))
|| !(rc->rc_tp.t_iflag & IGNPAR))
&& (!(iack & RCSR_Break)
|| !(rc->rc_tp.t_iflag & IGNBRK)))) {
if ( (iack & (RCSR_PE|RCSR_FE))
&& (t_state & TS_CAN_BYPASS_L_RINT)
&& ((iack & RCSR_FE)
|| ((iack & RCSR_PE)
&& (rc->rc_tp.t_iflag & INPCK))))
val = 0;
else if (val != 0 && val == rc->rc_hotchar)
swi_sched(sc->sc_swicookie, 0);
optr[0] = val;
optr[INPUT_FLAGS_SHIFT] = iack;
optr++;
sc->sc_scheduled_event++;
}
}
}
rc->rc_iptr = optr;
rc->rc_flags |= RC_DORXFER;
} else
resid = ucnt;
/* Clear FIFO if necessary */
while (resid-- > 0) {
if (!good_data)
iack = rcin(sc, CD180_RCSR);
else
iack = 0;
if (iack & RCSR_Timeout)
break;
(void) rcin(sc, CD180_RDR);
}
goto more_intrs;
}
if (bsr & RC_BSR_MOINT) {
iack = rcin(sc, RC_PILR_MODEM);
if (iack != (GIVR_IT_MSCI | RC_FAKEID)) {
device_printf(sc->sc_dev, "fake moint: %02x\n",
iack);
goto more_intrs;
}
chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
rc = &sc->sc_channels[chan];
iack = rcin(sc, CD180_MCR);
rc->rc_msvr = rcin(sc, CD180_MSVR);
rcout(sc, CD180_MCR, 0);
#ifdef RCDEBUG
printrcflags(rc, "moint");
#endif
if (rc->rc_flags & RC_CTSFLOW) {
if (rc->rc_msvr & MSVR_CTS)
rc->rc_flags |= RC_SEND_RDY;
else
rc->rc_flags &= ~RC_SEND_RDY;
} else
rc->rc_flags |= RC_SEND_RDY;
if ((iack & MCR_CDchg) && !(rc->rc_flags & RC_MODCHG)) {
sc->sc_scheduled_event += LOTS_OF_EVENTS;
rc->rc_flags |= RC_MODCHG;
swi_sched(sc->sc_swicookie, 0);
}
goto more_intrs;
}
if (bsr & RC_BSR_TXINT) {
iack = rcin(sc, RC_PILR_TX);
if (iack != (GIVR_IT_TDI | RC_FAKEID)) {
device_printf(sc->sc_dev, "fake txint: %02x\n",
iack);
goto more_intrs;
}
chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
rc = &sc->sc_channels[chan];
if ( (rc->rc_flags & RC_OSUSP)
|| !(rc->rc_flags & RC_SEND_RDY)
)
goto more_intrs;
/* Handle breaks and other stuff */
if (rc->rc_pendcmd) {
rcout(sc, CD180_COR2, rc->rc_cor2 |= COR2_ETC);
rcout(sc, CD180_TDR, CD180_C_ESC);
rcout(sc, CD180_TDR, rc->rc_pendcmd);
rcout(sc, CD180_COR2, rc->rc_cor2 &= ~COR2_ETC);
rc->rc_pendcmd = 0;
goto more_intrs;
}
optr = rc->rc_optr;
resid = rc->rc_obufend - optr;
if (resid > CD180_NFIFO)
resid = CD180_NFIFO;
while (resid-- > 0)
rcout(sc, CD180_TDR, *optr++);
rc->rc_optr = optr;
/* output completed? */
if (optr >= rc->rc_obufend) {
rcout(sc, CD180_IER, rc->rc_ier &= ~IER_TxRdy);
#ifdef RCDEBUG
device_printf(sc->sc_dev,
"channel %d: output completed\n",
rc->rc_chan);
#endif
if (!(rc->rc_flags & RC_DOXXFER)) {
sc->sc_scheduled_event += LOTS_OF_EVENTS;
rc->rc_flags |= RC_DOXXFER;
swi_sched(sc->sc_swicookie, 0);
}
}
}
more_intrs:
rcout(sc, CD180_EOIR, 0); /* end of interrupt */
rcout(sc, RC_CTOUT, 0);
bsr = ~(rcin(sc, RC_BSR));
}
}
/* Feed characters to output buffer */
static void
rc_start(struct tty *tp)
{
struct rc_softc *sc;
struct rc_chans *rc;
int s;
rc = TTY_TO_RC(tp);
if (rc->rc_flags & RC_OSBUSY)
return;
sc = rc->rc_rcb;
s = spltty();
rc->rc_flags |= RC_OSBUSY;
critical_enter();
if (tp->t_state & TS_TTSTOP)
rc->rc_flags |= RC_OSUSP;
else
rc->rc_flags &= ~RC_OSUSP;
/* Do RTS flow control stuff */
if ( (rc->rc_flags & RC_RTSFLOW)
&& (tp->t_state & TS_TBLOCK)
&& (rc->rc_msvr & MSVR_RTS)
) {
rcout(sc, CD180_CAR, rc->rc_chan);
rcout(sc, CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS);
} else if (!(rc->rc_msvr & MSVR_RTS)) {
rcout(sc, CD180_CAR, rc->rc_chan);
rcout(sc, CD180_MSVR, rc->rc_msvr |= MSVR_RTS);
}
critical_exit();
if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
goto out;
#ifdef RCDEBUG
printrcflags(rc, "rcstart");
#endif
ttwwakeup(tp);
#ifdef RCDEBUG
printf("rcstart: outq = %d obuf = %d\n",
tp->t_outq.c_cc, rc->rc_obufend - rc->rc_optr);
#endif
if (tp->t_state & TS_BUSY)
goto out; /* output still in progress ... */
if (tp->t_outq.c_cc > 0) {
u_int ocnt;
tp->t_state |= TS_BUSY;
ocnt = q_to_b(&tp->t_outq, rc->rc_obuf, sizeof rc->rc_obuf);
critical_enter();
rc->rc_optr = rc->rc_obuf;
rc->rc_obufend = rc->rc_optr + ocnt;
critical_exit();
if (!(rc->rc_ier & IER_TxRdy)) {
#ifdef RCDEBUG
device_printf(sc->sc_dev,
"channel %d: rcstart enable txint\n", rc->rc_chan);
#endif
rcout(sc, CD180_CAR, rc->rc_chan);
rcout(sc, CD180_IER, rc->rc_ier |= IER_TxRdy);
}
}
out:
rc->rc_flags &= ~RC_OSBUSY;
(void) splx(s);
}
/* Handle delayed events. */
void
rc_pollcard(void *arg)
{
struct rc_softc *sc;
struct rc_chans *rc;
struct tty *tp;
u_char *tptr, *eptr;
int chan, icnt;
sc = (struct rc_softc *)arg;
if (sc->sc_scheduled_event == 0)
return;
do {
rc = sc->sc_channels;
for (chan = 0; chan < CD180_NCHAN; rc++, chan++) {
tp = &rc->rc_tp;
#ifdef RCDEBUG
if (rc->rc_flags & (RC_DORXFER|RC_DOXXFER|RC_MODCHG|
RC_WAS_BUFOVFL|RC_WAS_SILOVFL))
printrcflags(rc, "rcevent");
#endif
if (rc->rc_flags & RC_WAS_BUFOVFL) {
critical_enter();
rc->rc_flags &= ~RC_WAS_BUFOVFL;
sc->sc_scheduled_event--;
critical_exit();
device_printf(sc->sc_dev,
"channel %d: interrupt-level buffer overflow\n",
chan);
}
if (rc->rc_flags & RC_WAS_SILOVFL) {
critical_enter();
rc->rc_flags &= ~RC_WAS_SILOVFL;
sc->sc_scheduled_event--;
critical_exit();
device_printf(sc->sc_dev,
"channel %d: silo overflow\n", chan);
}
if (rc->rc_flags & RC_MODCHG) {
critical_enter();
rc->rc_flags &= ~RC_MODCHG;
sc->sc_scheduled_event -= LOTS_OF_EVENTS;
critical_exit();
(*linesw[tp->t_line].l_modem)(tp, !!(rc->rc_msvr & MSVR_CD));
}
if (rc->rc_flags & RC_DORXFER) {
critical_enter();
rc->rc_flags &= ~RC_DORXFER;
eptr = rc->rc_iptr;
if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE])
tptr = &rc->rc_ibuf[RC_IBUFSIZE];
else
tptr = rc->rc_ibuf;
icnt = eptr - tptr;
if (icnt > 0) {
if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) {
rc->rc_iptr = rc->rc_ibuf;
rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE];
rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER];
} else {
rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE];
rc->rc_bufend = &rc->rc_ibuf[2 * RC_IBUFSIZE];
rc->rc_hiwat =
&rc->rc_ibuf[RC_IBUFSIZE + RC_IHIGHWATER];
}
if ( (rc->rc_flags & RC_RTSFLOW)
&& (tp->t_state & TS_ISOPEN)
&& !(tp->t_state & TS_TBLOCK)
&& !(rc->rc_msvr & MSVR_RTS)
) {
rcout(sc, CD180_CAR, chan);
rcout(sc, CD180_MSVR,
rc->rc_msvr |= MSVR_RTS);
}
sc->sc_scheduled_event -= icnt;
}
critical_exit();
if (icnt <= 0 || !(tp->t_state & TS_ISOPEN))
goto done1;
if ( (tp->t_state & TS_CAN_BYPASS_L_RINT)
&& !(tp->t_state & TS_LOCAL)) {
if ((tp->t_rawq.c_cc + icnt) >= RB_I_HIGH_WATER
&& ((rc->rc_flags & RC_RTSFLOW) || (tp->t_iflag & IXOFF))
&& !(tp->t_state & TS_TBLOCK))
ttyblock(tp);
tk_nin += icnt;
tk_rawcc += icnt;
tp->t_rawcc += icnt;
if (b_to_q(tptr, icnt, &tp->t_rawq))
device_printf(sc->sc_dev,
"channel %d: tty-level buffer overflow\n",
chan);
ttwakeup(tp);
if ((tp->t_state & TS_TTSTOP) && ((tp->t_iflag & IXANY)
|| (tp->t_cc[VSTART] == tp->t_cc[VSTOP]))) {
tp->t_state &= ~TS_TTSTOP;
tp->t_lflag &= ~FLUSHO;
rc_start(tp);
}
} else {
for (; tptr < eptr; tptr++)
(*linesw[tp->t_line].l_rint)
(tptr[0] |
rc_rcsrt[tptr[INPUT_FLAGS_SHIFT] & 0xF], tp);
}
done1: ;
}
if (rc->rc_flags & RC_DOXXFER) {
critical_enter();
sc->sc_scheduled_event -= LOTS_OF_EVENTS;
rc->rc_flags &= ~RC_DOXXFER;
rc->rc_tp.t_state &= ~TS_BUSY;
critical_exit();
(*linesw[tp->t_line].l_start)(tp);
}
if (sc->sc_scheduled_event == 0)
break;
}
} while (sc->sc_scheduled_event >= LOTS_OF_EVENTS);
}
static void
rc_stop(struct tty *tp, int rw)
{
struct rc_softc *sc;
struct rc_chans *rc;
u_char *tptr, *eptr;
rc = TTY_TO_RC(tp);
sc = rc->rc_rcb;
#ifdef RCDEBUG
device_printf(sc->sc_dev, "channel %d: rc_stop %s%s\n",
rc->rc_chan, (rw & FWRITE)?"FWRITE ":"", (rw & FREAD)?"FREAD":"");
#endif
if (rw & FWRITE)
rc_discard_output(rc);
critical_enter();
if (rw & FREAD) {
rc->rc_flags &= ~RC_DORXFER;
eptr = rc->rc_iptr;
if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) {
tptr = &rc->rc_ibuf[RC_IBUFSIZE];
rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE];
} else {
tptr = rc->rc_ibuf;
rc->rc_iptr = rc->rc_ibuf;
}
sc->sc_scheduled_event -= eptr - tptr;
}
if (tp->t_state & TS_TTSTOP)
rc->rc_flags |= RC_OSUSP;
else
rc->rc_flags &= ~RC_OSUSP;
critical_exit();
}
static int
rcopen(dev_t dev, int flag, int mode, d_thread_t *td)
{
struct rc_softc *sc;
struct rc_chans *rc;
struct tty *tp;
int s, error = 0;
rc = DEV_TO_RC(dev);
sc = rc->rc_rcb;
tp = &rc->rc_tp;
if (sc->sc_opencount < 0)
return (ENXIO);
sc->sc_opencount++;
#ifdef RCDEBUG
device_printf(sc->sc_dev, "channel %d: rcopen: dev %p\n",
rc->rc_chan, dev);
#endif
s = spltty();
again:
while (rc->rc_flags & RC_DTR_OFF) {
error = tsleep(&(rc->rc_dtrwait), TTIPRI | PCATCH, "rcdtr", 0);
if (error != 0)
goto out;
}
if (tp->t_state & TS_ISOPEN) {
if (CALLOUT(dev)) {
if (!(rc->rc_flags & RC_ACTOUT)) {
error = EBUSY;
goto out;
}
} else {
if (rc->rc_flags & RC_ACTOUT) {
if (flag & O_NONBLOCK) {
error = EBUSY;
goto out;
}
error = tsleep(&rc->rc_rcb,
TTIPRI|PCATCH, "rcbi", 0);
if (error)
goto out;
goto again;
}
}
if (tp->t_state & TS_XCLUDE &&
suser(td)) {
error = EBUSY;
goto out;
}
} else {
tp->t_oproc = rc_start;
tp->t_param = rc_param;
tp->t_stop = rc_stop;
tp->t_dev = dev;
if (CALLOUT(dev))
tp->t_cflag |= CLOCAL;
else
tp->t_cflag &= ~CLOCAL;
error = rc_param(tp, &tp->t_termios);
if (error)
goto out;
(void) rc_modctl(rc, TIOCM_RTS|TIOCM_DTR, DMSET);
if ((rc->rc_msvr & MSVR_CD) || CALLOUT(dev))
(*linesw[tp->t_line].l_modem)(tp, 1);
}
if (!(tp->t_state & TS_CARR_ON) && !CALLOUT(dev)
&& !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
rc->rc_dcdwaits++;
error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rcdcd", 0);
rc->rc_dcdwaits--;
if (error != 0)
goto out;
goto again;
}
error = (*linesw[tp->t_line].l_open)(dev, tp);
disc_optim(tp, &tp->t_termios, rc);
if ((tp->t_state & TS_ISOPEN) && CALLOUT(dev))
rc->rc_flags |= RC_ACTOUT;
out:
(void) splx(s);
if(rc->rc_dcdwaits == 0 && !(tp->t_state & TS_ISOPEN))
rc_hardclose(rc);
return error;
}
static int
rcclose(dev_t dev, int flag, int mode, d_thread_t *td)
{
struct rc_softc *sc;
struct rc_chans *rc;
struct tty *tp;
int s;
rc = DEV_TO_RC(dev);
sc = rc->rc_rcb;
tp = &rc->rc_tp;
#ifdef RCDEBUG
device_printf(sc->sc_dev, "channel %d: rcclose dev %p\n",
rc->rc_chan, dev);
#endif
s = spltty();
(*linesw[tp->t_line].l_close)(tp, flag);
disc_optim(tp, &tp->t_termios, rc);
rc_stop(tp, FREAD | FWRITE);
rc_hardclose(rc);
ttyclose(tp);
splx(s);
KASSERT(sc->sc_opencount > 0, ("rcclose: non-positive open count"));
sc->sc_opencount--;
return 0;
}
static void
rc_hardclose(struct rc_chans *rc)
{
struct rc_softc *sc;
struct tty *tp;
int s;
tp = &rc->rc_tp;
sc = rc->rc_rcb;
s = spltty();
rcout(sc, CD180_CAR, rc->rc_chan);
/* Disable rx/tx intrs */
rcout(sc, CD180_IER, rc->rc_ier = 0);
if ( (tp->t_cflag & HUPCL)
|| (!(rc->rc_flags & RC_ACTOUT)
&& !(rc->rc_msvr & MSVR_CD)
&& !(tp->t_cflag & CLOCAL))
|| !(tp->t_state & TS_ISOPEN)
) {
CCRCMD(sc, rc->rc_chan, CCR_ResetChan);
WAITFORCCR(sc, rc->rc_chan);
(void) rc_modctl(rc, TIOCM_RTS, DMSET);
if (rc->rc_dtrwait) {
callout_reset(&rc->rc_dtrcallout, rc->rc_dtrwait,
rc_dtrwakeup, rc);
rc->rc_flags |= RC_DTR_OFF;
}
}
rc->rc_flags &= ~RC_ACTOUT;
wakeup((caddr_t) &rc->rc_rcb); /* wake bi */
wakeup(TSA_CARR_ON(tp));
(void) splx(s);
}
/* Reset the bastard */
static void
rc_hwreset(struct rc_softc *sc, uint chipid)
{
CCRCMD(sc, -1, CCR_HWRESET); /* Hardware reset */
DELAY(20000);
WAITFORCCR(sc, -1);
rcout(sc, RC_CTOUT, 0); /* Clear timeout */
rcout(sc, CD180_GIVR, chipid);
rcout(sc, CD180_GICR, 0);
/* Set Prescaler Registers (1 msec) */
rcout(sc, CD180_PPRL, ((RC_OSCFREQ + 999) / 1000) & 0xFF);
rcout(sc, CD180_PPRH, ((RC_OSCFREQ + 999) / 1000) >> 8);
/* Initialize Priority Interrupt Level Registers */
rcout(sc, CD180_PILR1, RC_PILR_MODEM);
rcout(sc, CD180_PILR2, RC_PILR_TX);
rcout(sc, CD180_PILR3, RC_PILR_RX);
/* Reset DTR */
rcout(sc, RC_DTREG, ~0);
}
/* Set channel parameters */
static int
rc_param(struct tty *tp, struct termios *ts)
{
struct rc_softc *sc;
struct rc_chans *rc;
int idivs, odivs, s, val, cflag, iflag, lflag, inpflow;
if ( ts->c_ospeed < 0 || ts->c_ospeed > 76800
|| ts->c_ispeed < 0 || ts->c_ispeed > 76800
)
return (EINVAL);
if (ts->c_ispeed == 0)
ts->c_ispeed = ts->c_ospeed;
odivs = RC_BRD(ts->c_ospeed);
idivs = RC_BRD(ts->c_ispeed);
rc = TTY_TO_RC(tp);
sc = rc->rc_rcb;
s = spltty();
/* Select channel */
rcout(sc, CD180_CAR, rc->rc_chan);
/* If speed == 0, hangup line */
if (ts->c_ospeed == 0) {
CCRCMD(sc, rc->rc_chan, CCR_ResetChan);
WAITFORCCR(sc, rc->rc_chan);
(void) rc_modctl(rc, TIOCM_DTR, DMBIC);
}
tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
cflag = ts->c_cflag;
iflag = ts->c_iflag;
lflag = ts->c_lflag;
if (idivs > 0) {
rcout(sc, CD180_RBPRL, idivs & 0xFF);
rcout(sc, CD180_RBPRH, idivs >> 8);
}
if (odivs > 0) {
rcout(sc, CD180_TBPRL, odivs & 0xFF);
rcout(sc, CD180_TBPRH, odivs >> 8);
}
/* set timeout value */
if (ts->c_ispeed > 0) {
int itm = ts->c_ispeed > 2400 ? 5 : 10000 / ts->c_ispeed + 1;
if ( !(lflag & ICANON)
&& ts->c_cc[VMIN] != 0 && ts->c_cc[VTIME] != 0
&& ts->c_cc[VTIME] * 10 > itm)
itm = ts->c_cc[VTIME] * 10;
rcout(sc, CD180_RTPR, itm <= 255 ? itm : 255);
}
switch (cflag & CSIZE) {
case CS5: val = COR1_5BITS; break;
case CS6: val = COR1_6BITS; break;
case CS7: val = COR1_7BITS; break;
default:
case CS8: val = COR1_8BITS; break;
}
if (cflag & PARENB) {
val |= COR1_NORMPAR;
if (cflag & PARODD)
val |= COR1_ODDP;
if (!(cflag & INPCK))
val |= COR1_Ignore;
} else
val |= COR1_Ignore;
if (cflag & CSTOPB)
val |= COR1_2SB;
rcout(sc, CD180_COR1, val);
/* Set FIFO threshold */
val = ts->c_ospeed <= 4800 ? 1 : CD180_NFIFO / 2;
inpflow = 0;
if ( (iflag & IXOFF)
&& ( ts->c_cc[VSTOP] != _POSIX_VDISABLE
&& ( ts->c_cc[VSTART] != _POSIX_VDISABLE
|| (iflag & IXANY)
)
)
) {
inpflow = 1;
val |= COR3_SCDE|COR3_FCT;
}
rcout(sc, CD180_COR3, val);
/* Initialize on-chip automatic flow control */
val = 0;
rc->rc_flags &= ~(RC_CTSFLOW|RC_SEND_RDY);
if (cflag & CCTS_OFLOW) {
rc->rc_flags |= RC_CTSFLOW;
val |= COR2_CtsAE;
} else
rc->rc_flags |= RC_SEND_RDY;
if (tp->t_state & TS_TTSTOP)
rc->rc_flags |= RC_OSUSP;
else
rc->rc_flags &= ~RC_OSUSP;
if (cflag & CRTS_IFLOW)
rc->rc_flags |= RC_RTSFLOW;
else
rc->rc_flags &= ~RC_RTSFLOW;
if (inpflow) {
if (ts->c_cc[VSTART] != _POSIX_VDISABLE)
rcout(sc, CD180_SCHR1, ts->c_cc[VSTART]);
rcout(sc, CD180_SCHR2, ts->c_cc[VSTOP]);
val |= COR2_TxIBE;
if (iflag & IXANY)
val |= COR2_IXM;
}
rcout(sc, CD180_COR2, rc->rc_cor2 = val);
CCRCMD(sc, rc->rc_chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
disc_optim(tp, ts, rc);
/* modem ctl */
val = cflag & CLOCAL ? 0 : MCOR1_CDzd;
if (cflag & CCTS_OFLOW)
val |= MCOR1_CTSzd;
rcout(sc, CD180_MCOR1, val);
val = cflag & CLOCAL ? 0 : MCOR2_CDod;
if (cflag & CCTS_OFLOW)
val |= MCOR2_CTSod;
rcout(sc, CD180_MCOR2, val);
/* enable i/o and interrupts */
CCRCMD(sc, rc->rc_chan,
CCR_XMTREN | ((cflag & CREAD) ? CCR_RCVREN : CCR_RCVRDIS));
WAITFORCCR(sc, rc->rc_chan);
rc->rc_ier = cflag & CLOCAL ? 0 : IER_CD;
if (cflag & CCTS_OFLOW)
rc->rc_ier |= IER_CTS;
if (cflag & CREAD)
rc->rc_ier |= IER_RxData;
if (tp->t_state & TS_BUSY)
rc->rc_ier |= IER_TxRdy;
if (ts->c_ospeed != 0)
rc_modctl(rc, TIOCM_DTR, DMBIS);
if ((cflag & CCTS_OFLOW) && (rc->rc_msvr & MSVR_CTS))
rc->rc_flags |= RC_SEND_RDY;
rcout(sc, CD180_IER, rc->rc_ier);
(void) splx(s);
return 0;
}
/* Re-initialize board after bogus interrupts */
static void
rc_reinit(struct rc_softc *sc)
{
struct rc_chans *rc;
int i;
rc_hwreset(sc, RC_FAKEID);
rc = sc->sc_channels;
for (i = 0; i < CD180_NCHAN; i++, rc++)
(void) rc_param(&rc->rc_tp, &rc->rc_tp.t_termios);
}
static int
rcioctl(dev_t dev, u_long cmd, caddr_t data, int flag, d_thread_t *td)
{
struct rc_chans *rc;
struct tty *tp;
int s, error;
rc = DEV_TO_RC(dev);
tp = &rc->rc_tp;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
if (error != ENOIOCTL)
return (error);
error = ttioctl(tp, cmd, data, flag);
disc_optim(tp, &tp->t_termios, rc);
if (error != ENOIOCTL)
return (error);
s = spltty();
switch (cmd) {
case TIOCSBRK:
rc->rc_pendcmd = CD180_C_SBRK;
break;
case TIOCCBRK:
rc->rc_pendcmd = CD180_C_EBRK;
break;
case TIOCSDTR:
(void) rc_modctl(rc, TIOCM_DTR, DMBIS);
break;
case TIOCCDTR:
(void) rc_modctl(rc, TIOCM_DTR, DMBIC);
break;
case TIOCMGET:
*(int *) data = rc_modctl(rc, 0, DMGET);
break;
case TIOCMSET:
(void) rc_modctl(rc, *(int *) data, DMSET);
break;
case TIOCMBIC:
(void) rc_modctl(rc, *(int *) data, DMBIC);
break;
case TIOCMBIS:
(void) rc_modctl(rc, *(int *) data, DMBIS);
break;
case TIOCMSDTRWAIT:
error = suser(td);
if (error != 0) {
splx(s);
return (error);
}
rc->rc_dtrwait = *(int *)data * hz / 100;
break;
case TIOCMGDTRWAIT:
*(int *)data = rc->rc_dtrwait * 100 / hz;
break;
default:
(void) splx(s);
return ENOTTY;
}
(void) splx(s);
return 0;
}
/* Modem control routines */
static int
rc_modctl(struct rc_chans *rc, int bits, int cmd)
{
struct rc_softc *sc;
u_char *dtr;
u_char msvr;
sc = rc->rc_rcb;
dtr = &sc->sc_dtr;
rcout(sc, CD180_CAR, rc->rc_chan);
switch (cmd) {
case DMSET:
rcout(sc, RC_DTREG, (bits & TIOCM_DTR) ?
~(*dtr |= 1 << rc->rc_chan) :
~(*dtr &= ~(1 << rc->rc_chan)));
msvr = rcin(sc, CD180_MSVR);
if (bits & TIOCM_RTS)
msvr |= MSVR_RTS;
else
msvr &= ~MSVR_RTS;
if (bits & TIOCM_DTR)
msvr |= MSVR_DTR;
else
msvr &= ~MSVR_DTR;
rcout(sc, CD180_MSVR, msvr);
break;
case DMBIS:
if (bits & TIOCM_DTR)
rcout(sc, RC_DTREG, ~(*dtr |= 1 << rc->rc_chan));
msvr = rcin(sc, CD180_MSVR);
if (bits & TIOCM_RTS)
msvr |= MSVR_RTS;
if (bits & TIOCM_DTR)
msvr |= MSVR_DTR;
rcout(sc, CD180_MSVR, msvr);
break;
case DMGET:
bits = TIOCM_LE;
msvr = rc->rc_msvr = rcin(sc, CD180_MSVR);
if (msvr & MSVR_RTS)
bits |= TIOCM_RTS;
if (msvr & MSVR_CTS)
bits |= TIOCM_CTS;
if (msvr & MSVR_DSR)
bits |= TIOCM_DSR;
if (msvr & MSVR_DTR)
bits |= TIOCM_DTR;
if (msvr & MSVR_CD)
bits |= TIOCM_CD;
if (~rcin(sc, RC_RIREG) & (1 << rc->rc_chan))
bits |= TIOCM_RI;
return bits;
case DMBIC:
if (bits & TIOCM_DTR)
rcout(sc, RC_DTREG, ~(*dtr &= ~(1 << rc->rc_chan)));
msvr = rcin(sc, CD180_MSVR);
if (bits & TIOCM_RTS)
msvr &= ~MSVR_RTS;
if (bits & TIOCM_DTR)
msvr &= ~MSVR_DTR;
rcout(sc, CD180_MSVR, msvr);
break;
}
rc->rc_msvr = rcin(sc, CD180_MSVR);
return 0;
}
#define ERR(s) do { \
device_printf(sc->sc_dev, "%s", ""); \
printf s ; \
printf("\n"); \
(void) splx(old_level); \
return 1; \
} while (0)
/* Test the board. */
int
rc_test(struct rc_softc *sc)
{
int chan = 0;
int i = 0, rcnt, old_level;
unsigned int iack, chipid;
unsigned short divs;
static u_char ctest[] = "\377\125\252\045\244\0\377";
#define CTLEN 8
struct rtest {
u_char txbuf[CD180_NFIFO]; /* TX buffer */
u_char rxbuf[CD180_NFIFO]; /* RX buffer */
int rxptr; /* RX pointer */
int txptr; /* TX pointer */
} tchans[CD180_NCHAN];
old_level = spltty();
chipid = RC_FAKEID;
/* First, reset board to inital state */
rc_hwreset(sc, chipid);
divs = RC_BRD(19200);
/* Initialize channels */
for (chan = 0; chan < CD180_NCHAN; chan++) {
/* Select and reset channel */
rcout(sc, CD180_CAR, chan);
CCRCMD(sc, chan, CCR_ResetChan);
WAITFORCCR(sc, chan);
/* Set speed */
rcout(sc, CD180_RBPRL, divs & 0xFF);
rcout(sc, CD180_RBPRH, divs >> 8);
rcout(sc, CD180_TBPRL, divs & 0xFF);
rcout(sc, CD180_TBPRH, divs >> 8);
/* set timeout value */
rcout(sc, CD180_RTPR, 0);
/* Establish local loopback */
rcout(sc, CD180_COR1, COR1_NOPAR | COR1_8BITS | COR1_1SB);
rcout(sc, CD180_COR2, COR2_LLM);
rcout(sc, CD180_COR3, CD180_NFIFO);
CCRCMD(sc, chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
CCRCMD(sc, chan, CCR_RCVREN | CCR_XMTREN);
WAITFORCCR(sc, chan);
rcout(sc, CD180_MSVR, MSVR_RTS);
/* Fill TXBUF with test data */
for (i = 0; i < CD180_NFIFO; i++) {
tchans[chan].txbuf[i] = ctest[i];
tchans[chan].rxbuf[i] = 0;
}
tchans[chan].txptr = tchans[chan].rxptr = 0;
/* Now, start transmit */
rcout(sc, CD180_IER, IER_TxMpty|IER_RxData);
}
/* Pseudo-interrupt poll stuff */
for (rcnt = 10000; rcnt-- > 0; rcnt--) {
i = ~(rcin(sc, RC_BSR));
if (i & RC_BSR_TOUT)
ERR(("BSR timeout bit set\n"));
else if (i & RC_BSR_TXINT) {
iack = rcin(sc, RC_PILR_TX);
if (iack != (GIVR_IT_TDI | chipid))
ERR(("Bad TX intr ack (%02x != %02x)\n",
iack, GIVR_IT_TDI | chipid));
chan = (rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH;
/* If no more data to transmit, disable TX intr */
if (tchans[chan].txptr >= CD180_NFIFO) {
iack = rcin(sc, CD180_IER);
rcout(sc, CD180_IER, iack & ~IER_TxMpty);
} else {
for (iack = tchans[chan].txptr;
iack < CD180_NFIFO; iack++)
rcout(sc, CD180_TDR,
tchans[chan].txbuf[iack]);
tchans[chan].txptr = iack;
}
rcout(sc, CD180_EOIR, 0);
} else if (i & RC_BSR_RXINT) {
u_char ucnt;
iack = rcin(sc, RC_PILR_RX);
if (iack != (GIVR_IT_RGDI | chipid) &&
iack != (GIVR_IT_REI | chipid))
ERR(("Bad RX intr ack (%02x != %02x)\n",
iack, GIVR_IT_RGDI | chipid));
chan = (rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH;
ucnt = rcin(sc, CD180_RDCR) & 0xF;
while (ucnt-- > 0) {
iack = rcin(sc, CD180_RCSR);
if (iack & RCSR_Timeout)
break;
if (iack & 0xF)
ERR(("Bad char chan %d (RCSR = %02X)\n",
chan, iack));
if (tchans[chan].rxptr > CD180_NFIFO)
ERR(("Got extra chars chan %d\n",
chan));
tchans[chan].rxbuf[tchans[chan].rxptr++] =
rcin(sc, CD180_RDR);
}
rcout(sc, CD180_EOIR, 0);
}
rcout(sc, RC_CTOUT, 0);
for (iack = chan = 0; chan < CD180_NCHAN; chan++)
if (tchans[chan].rxptr >= CD180_NFIFO)
iack++;
if (iack == CD180_NCHAN)
break;
}
for (chan = 0; chan < CD180_NCHAN; chan++) {
/* Select and reset channel */
rcout(sc, CD180_CAR, chan);
CCRCMD(sc, chan, CCR_ResetChan);
}
if (!rcnt)
ERR(("looses characters during local loopback\n"));
/* Now, check data */
for (chan = 0; chan < CD180_NCHAN; chan++)
for (i = 0; i < CD180_NFIFO; i++)
if (ctest[i] != tchans[chan].rxbuf[i])
ERR(("data mismatch chan %d ptr %d (%d != %d)\n",
chan, i, ctest[i], tchans[chan].rxbuf[i]));
(void) splx(old_level);
return 0;
}
#ifdef RCDEBUG
static void
printrcflags(struct rc_chans *rc, char *comment)
{
struct rc_softc *sc;
u_short f = rc->rc_flags;
sc = rc->rc_rcb;
printf("rc%d/%d: %s flags: %s%s%s%s%s%s%s%s%s%s%s%s\n",
rc->rc_rcb->rcb_unit, rc->rc_chan, comment,
(f & RC_DTR_OFF)?"DTR_OFF " :"",
(f & RC_ACTOUT) ?"ACTOUT " :"",
(f & RC_RTSFLOW)?"RTSFLOW " :"",
(f & RC_CTSFLOW)?"CTSFLOW " :"",
(f & RC_DORXFER)?"DORXFER " :"",
(f & RC_DOXXFER)?"DOXXFER " :"",
(f & RC_MODCHG) ?"MODCHG " :"",
(f & RC_OSUSP) ?"OSUSP " :"",
(f & RC_OSBUSY) ?"OSBUSY " :"",
(f & RC_WAS_BUFOVFL) ?"BUFOVFL " :"",
(f & RC_WAS_SILOVFL) ?"SILOVFL " :"",
(f & RC_SEND_RDY) ?"SEND_RDY":"");
rcout(sc, CD180_CAR, rc->rc_chan);
printf("rc%d/%d: msvr %02x ier %02x ccsr %02x\n",
rc->rc_rcb->rcb_unit, rc->rc_chan,
rcin(sc, CD180_MSVR),
rcin(sc, CD180_IER),
rcin(sc, CD180_CCSR));
}
#endif /* RCDEBUG */
static void
rc_dtrwakeup(void *arg)
{
struct rc_chans *rc;
rc = (struct rc_chans *)arg;
rc->rc_flags &= ~RC_DTR_OFF;
wakeup(&rc->rc_dtrwait);
}
static void
rc_discard_output(struct rc_chans *rc)
{
critical_enter();
if (rc->rc_flags & RC_DOXXFER) {
rc->rc_rcb->sc_scheduled_event -= LOTS_OF_EVENTS;
rc->rc_flags &= ~RC_DOXXFER;
}
rc->rc_optr = rc->rc_obufend;
rc->rc_tp.t_state &= ~TS_BUSY;
critical_exit();
ttwwakeup(&rc->rc_tp);
}
static void
disc_optim(struct tty *tp, struct termios *t, struct rc_chans *rc)
{
if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
&& (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
&& (!(t->c_iflag & PARMRK)
|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
&& !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
&& linesw[tp->t_line].l_rint == ttyinput)
tp->t_state |= TS_CAN_BYPASS_L_RINT;
else
tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
rc->rc_hotchar = linesw[tp->t_line].l_hotchar;
}
static void
rc_wait0(struct rc_softc *sc, int chan, int line)
{
int rcnt;
for (rcnt = 50; rcnt && rcin(sc, CD180_CCR); rcnt--)
DELAY(30);
if (rcnt == 0)
device_printf(sc->sc_dev,
"channel %d command timeout, rc.c line: %d\n", chan, line);
}
static device_method_t rc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, rc_probe),
DEVMETHOD(device_attach, rc_attach),
DEVMETHOD(device_detach, rc_detach),
{ 0, 0 }
};
static driver_t rc_driver = {
"rc",
rc_methods, sizeof(struct rc_softc),
};
DRIVER_MODULE(rc, isa, rc_driver, rc_devclass, 0, 0);