1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-24 11:29:10 +00:00
freebsd/sys/i386/isa/rc.c
Bruce Evans f3b37f91c1 Improve input flow control.
Use input buffer watermarks of TTYHOG-512 (high) and (high)*7/8
(low) instead of TTYHOG/2 (high) and TTYHOG/5 (low) to agree with
some drivers.  512 is magic and some things depended on TTYHOG/2
>= TTYHOG-512 to work; now they depend on the 512 magic not changing
and TTYHOG-512 being significantly larger than 0.  This should be
handled in ttsetwater().

Separate the decision about whether to do input flow control from
doing it.  ttyblock() now just starts input flow control (hardware
and/or software) and there is a new function ttyunblock() to stop
it.  The decisions are the same except for the watermark changes
and allowing for input expansion for PARMRK.

When flushing input, try harder at first to send a start character
if required, but give up if the first attempt fails.

cy.c, rc.c, sio.c:
Simplify: let ttyinput() handle input flow control if it is not
being bypassed.  Use ttyblock() to start flow control otherwise.

rc.c:
Use same input flow control test as elsewhere: test in a more
efficient order and start flow control at >= highwater instead of
at > highwater.
1995-07-31 18:29:51 +00:00

1516 lines
40 KiB
C

/*
* Copyright (C) 1995 by Pavel Antonov, Moscow, Russia.
* Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia.
* 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.
*/
/*
* SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver
*
*/
#include "rc.h"
#if NRC > 0
/*#define RCDEBUG*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/conf.h>
#include <sys/dkstat.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/devconf.h>
#include <machine/clock.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/sioreg.h>
#include <i386/isa/ic/cd180.h>
#include <i386/isa/rcreg.h>
/* Prototypes */
int rcprobe __P((struct isa_device *));
int rcattach __P((struct isa_device *));
int rcopen __P((dev_t, int, int, struct proc *));
int rcclose __P((dev_t, int, int, struct proc *));
int rcread __P((dev_t, struct uio *, int));
int rcwrite __P((dev_t, struct uio *, int));
void rcintr __P((int));
void rcpoll __P((void));
void rcstop __P((struct tty *, int));
int rcioctl __P((dev_t, int, caddr_t, int, struct proc *));
#define rcin(port) RC_IN (nec, port)
#define rcout(port,v) RC_OUT (nec, port, v)
#define WAITFORCCR(u,c) rc_wait0(nec, (u), (c), __LINE__)
#define CCRCMD(u,c,cmd) WAITFORCCR((u), (c)); rcout(CD180_CCR, (cmd))
#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 RC_PROBED 1
#define RC_ATTACHED 2
#define GET_UNIT(dev) (minor(dev) & 0x3F)
#define CALLOUT(dev) (minor(dev) & 0x80)
/* For isa routines */
struct isa_driver rcdriver = {
rcprobe, rcattach, "rc"
};
/* Per-board structure */
static struct rc_softc {
u_int rcb_probed; /* 1 - probed, 2 - attached */
u_int rcb_addr; /* Base I/O addr */
u_int rcb_unit; /* unit # */
u_char rcb_dtr; /* DTR status */
struct rc_chans *rcb_baserc; /* base rc ptr */
} rc_softc[NRC];
/* Per-channel structure */
static struct rc_chans {
struct rc_softc *rc_rcb; /* back ptr */
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 */
} rc_chans[NRC * CD180_NCHAN];
static int rc_scheduled_event = 0;
/* for pstat -t */
struct tty rc_tty[NRC * CD180_NCHAN];
int nrc_tty = NRC * CD180_NCHAN;
/* 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 */
static struct speedtab rc_speedtab[] = {
0, 0,
50, RC_BRD(50),
75, RC_BRD(75),
110, RC_BRD(110),
134, RC_BRD(134),
150, RC_BRD(150),
200, RC_BRD(200),
300, RC_BRD(300),
600, RC_BRD(600),
1200, RC_BRD(1200),
1800, RC_BRD(1800),
2400, RC_BRD(2400),
4800, RC_BRD(4800),
9600, RC_BRD(9600),
19200, RC_BRD(19200),
38400, RC_BRD(38400),
57600, RC_BRD(57600),
/* real max value is 76800 with 9.8304 MHz clock */
-1, -1
};
/* 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 prototypes */
static void rc_hwreset __P((int, int, unsigned int));
static int rc_test __P((int, int));
static void rc_discard_output __P((struct rc_chans *));
static void rc_hardclose __P((struct rc_chans *));
static int rc_modctl __P((struct rc_chans *, int, int));
static void rc_start __P((struct tty *));
static int rc_param __P((struct tty *, struct termios *));
static void rc_registerdev __P((struct isa_device *id));
static void rc_reinit __P((struct rc_softc *));
#ifdef RCDEBUG
static void printrcflags();
#endif
static timeout_t rc_dtrwakeup;
static timeout_t rc_wakeup;
static void disc_optim __P((struct tty *tp, struct termios *t, struct rc_chans *));
static void rc_wait0 __P((int nec, int unit, int chan, int line));
/**********************************************/
/* Quick device probing */
int rcprobe(dvp)
struct isa_device *dvp;
{
int irq = ffs(dvp->id_irq) - 1;
register int nec = dvp->id_iobase;
if (dvp->id_unit > NRC)
return 0;
if (!RC_VALIDADDR(nec)) {
printf("rc%d: illegal base address %x\n", nec);
return 0;
}
if (!RC_VALIDIRQ(irq)) {
printf("rc%d: illegal IRQ value %d\n", irq);
return 0;
}
rcout(CD180_PPRL, 0x22); /* Random values to Prescale reg. */
rcout(CD180_PPRH, 0x11);
if (rcin(CD180_PPRL) != 0x22 || rcin(CD180_PPRH) != 0x11)
return 0;
/* Now, test the board more thoroughly, with diagnostic */
if (rc_test(nec, dvp->id_unit))
return 0;
rc_softc[dvp->id_unit].rcb_probed = RC_PROBED;
return 0xF;
}
static struct kern_devconf kdc_rc[NRC] = { {
0, 0, 0, /* filled in by dev_attach */
"rc", 0, { MDDT_ISA, 0, "tty" },
isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
&kdc_isa0, /* parent */
0, /* parentdata */
DC_UNCONFIGURED, /* state */
"RISCom/8 multiport card",
DC_CLS_SERIAL /* class */
} };
static void
rc_registerdev(id)
struct isa_device *id;
{
int unit;
unit = id->id_unit;
if (unit != 0)
kdc_rc[unit] = kdc_rc[0];
kdc_rc[unit].kdc_unit = unit;
kdc_rc[unit].kdc_isa = id;
kdc_rc[unit].kdc_state = DC_UNKNOWN;
dev_attach(&kdc_rc[unit]);
}
int rcattach(dvp)
struct isa_device *dvp;
{
register int i, chan, nec = dvp->id_iobase;
struct rc_softc *rcb = &rc_softc[dvp->id_unit];
struct rc_chans *rc = &rc_chans[dvp->id_unit * CD180_NCHAN];
static int rc_wakeup_started = 0;
struct tty *tp;
/* Thorooughly test the device */
if (rcb->rcb_probed != RC_PROBED)
return 0;
rcb->rcb_addr = nec;
rcb->rcb_dtr = 0;
rcb->rcb_baserc = rc;
/*rcb->rcb_chipid = 0x10 + dvp->id_unit;*/
printf("rc%d: %d chans, firmware rev. %c\n", dvp->id_unit,
CD180_NCHAN, (rcin(CD180_GFRCR) & 0xF) + 'A');
rc_registerdev(dvp);
for (chan = 0; chan < CD180_NCHAN; chan++, rc++) {
rc->rc_rcb = rcb;
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_flags = rc->rc_ier = rc->rc_msvr = 0;
rc->rc_cor2 = rc->rc_pendcmd = 0;
rc->rc_optr = rc->rc_obufend = rc->rc_obuf;
rc->rc_dtrwait = 3 * hz;
rc->rc_dcdwaits= 0;
rc->rc_hotchar = 0;
tp = rc->rc_tp = &rc_tty[chan];
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;
}
rcb->rcb_probed = RC_ATTACHED;
if (!rc_wakeup_started) {
rc_wakeup((void *)NULL);
rc_wakeup_started = 0;
}
return 1;
}
/* RC interrupt handling */
void rcintr(unit)
int unit;
{
register struct rc_softc *rcb = &rc_softc[unit];
register struct rc_chans *rc;
register int nec, resid;
register u_char val, iack, bsr, ucnt, *optr;
int good_data, t_state;
if (rcb->rcb_probed != RC_ATTACHED) {
printf("rc%d: bogus interrupt\n", unit);
return;
}
nec = rcb->rcb_addr;
bsr = ~(rcin(RC_BSR));
if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) {
printf("rc%d: extra interrupt\n", unit);
rcout(CD180_EOIR, 0);
return;
}
while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) {
#ifdef RCDEBUG_DETAILED
printf("rc%d: intr (%02x) %s%s%s%s\n", unit, 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) {
printf("rc%d: hardware failure, reset board\n", unit);
rcout(RC_CTOUT, 0);
rc_reinit(rcb);
return;
}
if (bsr & RC_BSR_RXINT) {
iack = rcin(RC_PILR_RX);
good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID));
if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) {
printf("rc%d: fake rxint: %02x\n", unit, iack);
goto more_intrs;
}
rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);
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(CD180_MSVR,
rc->rc_msvr &= ~MSVR_RTS);
else if (!(rc->rc_msvr & MSVR_RTS))
rcout(CD180_MSVR,
rc->rc_msvr |= MSVR_RTS);
}
ucnt = rcin(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;
rc_scheduled_event++;
}
}
optr = rc->rc_iptr;
/* check foor good data */
if (good_data) {
while (ucnt-- > 0) {
val = rcin(CD180_RDR);
optr[0] = val;
optr[INPUT_FLAGS_SHIFT] = 0;
optr++;
rc_scheduled_event++;
if (val != 0 && val == rc->rc_hotchar)
setsofttty();
}
} else {
/* Store also status data */
while (ucnt-- > 0) {
iack = rcin(CD180_RCSR);
if (iack & RCSR_Timeout)
break;
if ( (iack & RCSR_OE)
&& !(rc->rc_flags & RC_WAS_SILOVFL)) {
rc->rc_flags |= RC_WAS_SILOVFL;
rc_scheduled_event++;
}
val = rcin(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)
setsofttty();
optr[0] = val;
optr[INPUT_FLAGS_SHIFT] = iack;
optr++;
rc_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(CD180_RCSR);
else
iack = 0;
if (iack & RCSR_Timeout)
break;
(void) rcin(CD180_RDR);
}
goto more_intrs;
}
if (bsr & RC_BSR_MOINT) {
iack = rcin(RC_PILR_MODEM);
if (iack != (GIVR_IT_MSCI | RC_FAKEID)) {
printf("rc%d: fake moint: %02x\n", unit, iack);
goto more_intrs;
}
rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);
iack = rcin(CD180_MCR);
rc->rc_msvr = rcin(CD180_MSVR);
rcout(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)) {
rc_scheduled_event += LOTS_OF_EVENTS;
rc->rc_flags |= RC_MODCHG;
setsofttty();
}
goto more_intrs;
}
if (bsr & RC_BSR_TXINT) {
iack = rcin(RC_PILR_TX);
if (iack != (GIVR_IT_TDI | RC_FAKEID)) {
printf("rc%d: fake txint: %02x\n", unit, iack);
goto more_intrs;
}
rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);
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(CD180_COR2, rc->rc_cor2 |= COR2_ETC);
rcout(CD180_TDR, CD180_C_ESC);
rcout(CD180_TDR, rc->rc_pendcmd);
rcout(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(CD180_TDR, *optr++);
rc->rc_optr = optr;
/* output completed? */
if (optr >= rc->rc_obufend) {
rcout(CD180_IER, rc->rc_ier &= ~IER_TxRdy);
#ifdef RCDEBUG
printf("rc%d/%d: output completed\n", unit, rc->rc_chan);
#endif
if (!(rc->rc_flags & RC_DOXXFER)) {
rc_scheduled_event += LOTS_OF_EVENTS;
rc->rc_flags |= RC_DOXXFER;
setsofttty();
}
}
}
more_intrs:
rcout(CD180_EOIR, 0); /* end of interrupt */
rcout(RC_CTOUT, 0);
bsr = ~(rcin(RC_BSR));
}
}
/* Feed characters to output buffer */
static void rc_start(tp)
register struct tty *tp;
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)];
register int nec = rc->rc_rcb->rcb_addr, s;
if (rc->rc_flags & RC_OSBUSY)
return;
s = spltty();
rc->rc_flags |= RC_OSBUSY;
disable_intr();
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(CD180_CAR, rc->rc_chan);
rcout(CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS);
} else if (!(rc->rc_msvr & MSVR_RTS)) {
rcout(CD180_CAR, rc->rc_chan);
rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS);
}
enable_intr();
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);
disable_intr();
rc->rc_optr = rc->rc_obuf;
rc->rc_obufend = rc->rc_optr + ocnt;
enable_intr();
if (!(rc->rc_ier & IER_TxRdy)) {
#ifdef RCDEBUG
printf("rc%d/%d: rcstart enable txint\n", rc->rc_rcb->rcb_unit, rc->rc_chan);
#endif
rcout(CD180_CAR, rc->rc_chan);
rcout(CD180_IER, rc->rc_ier |= IER_TxRdy);
}
}
out:
rc->rc_flags &= ~RC_OSBUSY;
(void) splx(s);
}
/* Handle delayed events. */
void rcpoll()
{
register struct rc_chans *rc;
register struct rc_softc *rcb;
register u_char *tptr, *eptr;
register int s;
register struct tty *tp;
register int chan, icnt, c, nec, unit;
if (rc_scheduled_event == 0)
return;
repeat:
for (unit = 0; unit < NRC; unit++) {
rcb = &rc_softc[unit];
rc = rcb->rcb_baserc;
nec = rc->rc_rcb->rcb_addr;
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) {
disable_intr();
rc->rc_flags &= ~RC_WAS_BUFOVFL;
rc_scheduled_event--;
enable_intr();
printf("rc%d/%d: interrupt-level buffer overflow\n",
unit, chan);
}
if (rc->rc_flags & RC_WAS_SILOVFL) {
disable_intr();
rc->rc_flags &= ~RC_WAS_SILOVFL;
rc_scheduled_event--;
enable_intr();
printf("rc%d/%d: silo overflow\n",
unit, chan);
}
if (rc->rc_flags & RC_MODCHG) {
disable_intr();
rc->rc_flags &= ~RC_MODCHG;
rc_scheduled_event -= LOTS_OF_EVENTS;
enable_intr();
(*linesw[tp->t_line].l_modem)(tp, !!(rc->rc_msvr & MSVR_CD));
}
if (rc->rc_flags & RC_DORXFER) {
disable_intr();
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(CD180_CAR, chan);
rcout(CD180_MSVR,
rc->rc_msvr |= MSVR_RTS);
}
rc_scheduled_event -= icnt;
}
enable_intr();
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))
printf("rc%d/%d: tty-level buffer overflow\n",
unit, 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) {
disable_intr();
rc_scheduled_event -= LOTS_OF_EVENTS;
rc->rc_flags &= ~RC_DOXXFER;
rc->rc_tp->t_state &= ~TS_BUSY;
enable_intr();
(*linesw[tp->t_line].l_start)(tp);
}
}
if (rc_scheduled_event == 0)
break;
}
if (rc_scheduled_event >= LOTS_OF_EVENTS)
goto repeat;
}
void rcstop(tp, rw)
register struct tty *tp;
int rw;
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)];
u_char *tptr, *eptr;
#ifdef RCDEBUG
printf("rc%d/%d: rcstop %s%s\n", rc->rc_rcb->rcb_unit, rc->rc_chan,
(rw & FWRITE)?"FWRITE ":"", (rw & FREAD)?"FREAD":"");
#endif
if (rw & FWRITE)
rc_discard_output(rc);
disable_intr();
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;
}
rc_scheduled_event -= eptr - tptr;
}
if (tp->t_state & TS_TTSTOP)
rc->rc_flags |= RC_OSUSP;
else
rc->rc_flags &= ~RC_OSUSP;
enable_intr();
}
int rcopen(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
register struct rc_chans *rc;
register struct tty *tp;
int unit, nec, s, error = 0;
unit = GET_UNIT(dev);
if (unit >= NRC * CD180_NCHAN)
return ENXIO;
if (rc_softc[unit / CD180_NCHAN].rcb_probed != RC_ATTACHED)
return ENXIO;
rc = &rc_chans[unit];
tp = rc->rc_tp;
nec = rc->rc_rcb->rcb_addr;
#ifdef RCDEBUG
printf("rc%d/%d: rcopen: dev %x\n", rc->rc_rcb->rcb_unit, unit, 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;
}
if (error = tsleep(&rc->rc_rcb,
TTIPRI|PCATCH, "rcbi", 0))
goto out;
goto again;
}
}
if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
error = EBUSY;
goto out;
}
} else {
tp->t_oproc = rc_start;
tp->t_param = rc_param;
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);
ttsetwater(tp);
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);
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;
}
int rcclose(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
register struct rc_chans *rc;
register struct tty *tp;
int s, unit = GET_UNIT(dev);
if (unit >= NRC * CD180_NCHAN)
return ENXIO;
rc = &rc_chans[unit];
tp = rc->rc_tp;
#ifdef RCDEBUG
printf("rc%d/%d: rcclose dev %x\n", rc->rc_rcb->rcb_unit, unit, dev);
#endif
s = spltty();
(*linesw[tp->t_line].l_close)(tp, flag);
rcstop(tp, FREAD | FWRITE);
rc_hardclose(rc);
ttyclose(tp);
splx(s);
return 0;
}
static void rc_hardclose(rc)
register struct rc_chans *rc;
{
register int s, nec = rc->rc_rcb->rcb_addr;
register struct tty *tp = rc->rc_tp;
s = spltty();
rcout(CD180_CAR, rc->rc_chan);
/* Disable rx/tx intrs */
rcout(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(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan);
WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan);
(void) rc_modctl(rc, TIOCM_RTS, DMSET);
if (rc->rc_dtrwait) {
timeout(rc_dtrwakeup, rc, rc->rc_dtrwait);
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);
}
/* Read from line */
int rcread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp;
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
/* Write to line */
int rcwrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp;
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
/* Reset the bastard */
static void rc_hwreset(unit, nec, chipid)
register int unit, nec;
unsigned int chipid;
{
CCRCMD(unit, -1, CCR_HWRESET); /* Hardware reset */
DELAY(20000);
WAITFORCCR(unit, -1);
rcout(RC_CTOUT, 0); /* Clear timeout */
rcout(CD180_GIVR, chipid);
rcout(CD180_GICR, 0);
/* Set Prescaler Registers (1 msec) */
rcout(CD180_PPRL, ((RC_OSCFREQ + 999) / 1000) & 0xFF);
rcout(CD180_PPRH, ((RC_OSCFREQ + 999) / 1000) >> 8);
/* Initialize Priority Interrupt Level Registers */
rcout(CD180_PILR1, RC_PILR_MODEM);
rcout(CD180_PILR2, RC_PILR_TX);
rcout(CD180_PILR3, RC_PILR_RX);
/* Reset DTR */
rcout(RC_DTREG, ~0);
}
/* Set channel parameters */
static int rc_param(tp, ts)
register struct tty *tp;
struct termios *ts;
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)];
register int nec = rc->rc_rcb->rcb_addr;
int idivs, odivs, s, val, cflag, iflag, lflag, inpflow;
odivs = ttspeedtab(ts->c_ospeed, rc_speedtab);
if (ts->c_ispeed == 0)
ts->c_ispeed = ts->c_ospeed;
idivs = ttspeedtab(ts->c_ispeed, rc_speedtab);
if (idivs < 0 || odivs < 0)
return (EINVAL);
s = spltty();
/* Select channel */
rcout(CD180_CAR, rc->rc_chan);
/* If speed == 0, hangup line */
if (ts->c_ospeed == 0) {
CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan);
WAITFORCCR(rc->rc_rcb->rcb_unit, 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(CD180_RBPRL, idivs & 0xFF);
rcout(CD180_RBPRH, idivs >> 8);
}
if (odivs > 0) {
rcout(CD180_TBPRL, odivs & 0xFF);
rcout(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(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(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(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(CD180_SCHR1, ts->c_cc[VSTART]);
rcout(CD180_SCHR2, ts->c_cc[VSTOP]);
val |= COR2_TxIBE;
if (iflag & IXANY)
val |= COR2_IXM;
}
rcout(CD180_COR2, rc->rc_cor2 = val);
CCRCMD(rc->rc_rcb->rcb_unit, 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(CD180_MCOR1, val);
val = cflag & CLOCAL ? 0 : MCOR2_CDod;
if (cflag & CCTS_OFLOW)
val |= MCOR2_CTSod;
rcout(CD180_MCOR2, val);
/* enable i/o and interrupts */
CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan,
CCR_XMTREN | ((cflag & CREAD) ? CCR_RCVREN : CCR_RCVRDIS));
WAITFORCCR(rc->rc_rcb->rcb_unit, 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(CD180_IER, rc->rc_ier);
(void) splx(s);
return 0;
}
/* Re-initialize board after bogus interrupts */
static void rc_reinit(rcb)
struct rc_softc *rcb;
{
register struct rc_chans *rc, *rce;
register int i, nec;
nec = rcb->rcb_addr;
rc_hwreset(rcb->rcb_unit, nec, RC_FAKEID);
rc = &rc_chans[rcb->rcb_unit * CD180_NCHAN];
rce = rc + CD180_NCHAN;
for (; rc < rce; rc++)
(void) rc_param(rc->rc_tp, &rc->rc_tp->t_termios);
}
int rcioctl(dev, cmd, data, flag, p)
dev_t dev;
int cmd, flag;
caddr_t data;
struct proc *p;
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(dev)];
register int s, error;
struct tty *tp = rc->rc_tp;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag);
if (error >= 0)
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(p->p_ucred, &p->p_acflag);
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(rc, bits, cmd)
register struct rc_chans *rc;
int bits, cmd;
{
register int nec = rc->rc_rcb->rcb_addr;
u_char *dtr = &rc->rc_rcb->rcb_dtr, msvr;
rcout(CD180_CAR, rc->rc_chan);
switch (cmd) {
case DMSET:
rcout(RC_DTREG, (bits & TIOCM_DTR) ?
~(*dtr |= 1 << rc->rc_chan) :
~(*dtr &= ~(1 << rc->rc_chan)));
msvr = rcin(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(CD180_MSVR, msvr);
break;
case DMBIS:
if (bits & TIOCM_DTR)
rcout(RC_DTREG, ~(*dtr |= 1 << rc->rc_chan));
msvr = rcin(CD180_MSVR);
if (bits & TIOCM_RTS)
msvr |= MSVR_RTS;
if (bits & TIOCM_DTR)
msvr |= MSVR_DTR;
rcout(CD180_MSVR, msvr);
break;
case DMGET:
bits = TIOCM_LE;
msvr = rc->rc_msvr = rcin(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(RC_RIREG) & (1 << rc->rc_chan))
bits |= TIOCM_RI;
return bits;
case DMBIC:
if (bits & TIOCM_DTR)
rcout(RC_DTREG, ~(*dtr &= ~(1 << rc->rc_chan)));
msvr = rcin(CD180_MSVR);
if (bits & TIOCM_RTS)
msvr &= ~MSVR_RTS;
if (bits & TIOCM_DTR)
msvr &= ~MSVR_DTR;
rcout(CD180_MSVR, msvr);
break;
}
rc->rc_msvr = rcin(CD180_MSVR);
return 0;
}
/* Test the board. */
int rc_test(nec, unit)
register int nec;
int unit;
{
int chan = 0, nopt = 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
#define ERR(s) { \
printf("rc%d: ", unit); printf s ; printf("\n"); \
(void) splx(old_level); return 1; }
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(unit, nec, chipid);
divs = RC_BRD(19200);
/* Initialize channels */
for (chan = 0; chan < CD180_NCHAN; chan++) {
/* Select and reset channel */
rcout(CD180_CAR, chan);
CCRCMD(unit, chan, CCR_ResetChan);
WAITFORCCR(unit, chan);
/* Set speed */
rcout(CD180_RBPRL, divs & 0xFF);
rcout(CD180_RBPRH, divs >> 8);
rcout(CD180_TBPRL, divs & 0xFF);
rcout(CD180_TBPRH, divs >> 8);
/* set timeout value */
rcout(CD180_RTPR, 0);
/* Establish local loopback */
rcout(CD180_COR1, COR1_NOPAR | COR1_8BITS | COR1_1SB);
rcout(CD180_COR2, COR2_LLM);
rcout(CD180_COR3, CD180_NFIFO);
CCRCMD(unit, chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
CCRCMD(unit, chan, CCR_RCVREN | CCR_XMTREN);
WAITFORCCR(unit, chan);
rcout(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(CD180_IER, IER_TxMpty|IER_RxData);
}
/* Pseudo-interrupt poll stuff */
for (rcnt = 10000; rcnt-- > 0; rcnt--) {
i = ~(rcin(RC_BSR));
if (i & RC_BSR_TOUT)
ERR(("BSR timeout bit set\n"))
else if (i & RC_BSR_TXINT) {
iack = rcin(RC_PILR_TX);
if (iack != (GIVR_IT_TDI | chipid))
ERR(("Bad TX intr ack (%02x != %02x)\n",
iack, GIVR_IT_TDI | chipid));
chan = (rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH;
/* If no more data to transmit, disable TX intr */
if (tchans[chan].txptr >= CD180_NFIFO) {
iack = rcin(CD180_IER);
rcout(CD180_IER, iack & ~IER_TxMpty);
} else {
for (iack = tchans[chan].txptr;
iack < CD180_NFIFO; iack++)
rcout(CD180_TDR,
tchans[chan].txbuf[iack]);
tchans[chan].txptr = iack;
}
rcout(CD180_EOIR, 0);
} else if (i & RC_BSR_RXINT) {
u_char ucnt;
iack = rcin(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(CD180_GICR) & GICR_CHAN) >> GICR_LSH;
ucnt = rcin(CD180_RDCR) & 0xF;
while (ucnt-- > 0) {
iack = rcin(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(CD180_RDR);
}
rcout(CD180_EOIR, 0);
}
rcout(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(CD180_CAR, chan);
CCRCMD(unit, 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(rc, comment)
struct rc_chans *rc;
char *comment;
{
u_short f = rc->rc_flags;
register int nec = rc->rc_rcb->rcb_addr;
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(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(CD180_MSVR),
rcin(CD180_IER),
rcin(CD180_CCSR));
}
#endif /* RCDEBUG */
struct tty *
rcdevtotty(dev)
dev_t dev;
{
int unit;
unit = GET_UNIT(dev);
if (unit >= NRC * CD180_NCHAN)
return NULL;
return (&rc_tty[unit]);
}
static void
rc_dtrwakeup(chan)
void *chan;
{
struct rc_chans *rc;
rc = (struct rc_chans *)chan;
rc->rc_flags &= ~RC_DTR_OFF;
wakeup(&rc->rc_dtrwait);
}
static void
rc_discard_output(rc)
struct rc_chans *rc;
{
disable_intr();
if (rc->rc_flags & RC_DOXXFER) {
rc_scheduled_event -= LOTS_OF_EVENTS;
rc->rc_flags &= ~RC_DOXXFER;
}
rc->rc_optr = rc->rc_obufend;
rc->rc_tp->t_state &= ~TS_BUSY;
enable_intr();
ttwwakeup(rc->rc_tp);
}
static void
rc_wakeup(chan)
void *chan;
{
int unit;
timeout(rc_wakeup, (caddr_t)NULL, 1);
if (rc_scheduled_event != 0) {
int s;
s = splsofttty();
rcpoll();
splx(s);
}
}
static void
disc_optim(tp, t, rc)
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;
if (tp->t_line == SLIPDISC)
rc->rc_hotchar = 0xc0;
else if (tp->t_line == PPPDISC)
rc->rc_hotchar = 0x7e;
else
rc->rc_hotchar = 0;
}
static void
rc_wait0(nec, unit, chan, line)
int nec, unit, chan, line;
{
int rcnt;
for (rcnt = 100; rcnt && rcin(CD180_CCR); rcnt--)
DELAY(15);
if (rcnt == 0)
printf("rc%d/%d: channel command timeout, rc.c line: %d\n",
unit, chan, line);
}
#endif /* NRC */