Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

1570 lines
35 KiB
C

#ifndef lint
static char sccsid[] = "@(#)mti.c 1.1 92/07/30 SMI";
#endif
/*
* Copyright (c) 1987, 1989 by Sun Microsystems, Inc.
*/
/*
* Systech MTI-800/1600 Multiple Terminal Interface driver
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/ttold.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/tty.h>
#include <sys/clist.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/map.h>
#include <sys/varargs.h>
#include <sun/consdev.h>
#include <sundev/mbvar.h>
#include <sundev/mtireg.h>
#include <sundev/mtivar.h>
/*
* Driver information for auto-configuration stuff.
* Should be "void", not "int" - they return no values!
*/
static int mtiprobe(/*caddr_t reg*/);
static int mtiattach(/*struct mb_device *md*/);
int mtiintr(/*void*/);
extern struct mb_device *mtiinfo[];
struct mb_driver mtidriver = {
mtiprobe, 0, mtiattach, 0, 0, mtiintr,
sizeof (struct mtireg), "mti", mtiinfo, 0, 0, 0,
};
extern int nmti; /* number of MTI lines configured in */
#define OUTLINE 0x80 /* minor device flag for dialin/out on same line */
/*#define OUTLINE 0 /* disable dialin/out */
#define UNIT(x) (minor(x)&~OUTLINE)
#define BOARD(dev) (((dev)>>4)&0x7) /* board number for given dev */
#define LINE(dev) ((dev)&0xf) /* line number for given dev */
extern struct mtiline mtiline[];
#define MTIBUFSIZE 64 /* size of DMA buffers */
extern struct mti_softc mti_softc[];
static char mti_speeds[16] = {
0, /* B0 */
0, /* B50 */
1, /* B75 */
2, /* B110 */
3, /* B134 */
4, /* B150 */
9, /* B200 - MTI doesn't support, 2000 baud instead */
5, /* B300 */
6, /* B600 */
7, /* B1200 */
8, /* B1800 */
10, /* B2400 */
12, /* B4800 */
14, /* B9600 */
15, /* B19200 */
13 /* B38400/EXTB - MTI doesn't support, 7200 baud instead */
};
static char cmdlen[16] = { /* number of bytes in normal commands */
1, /* Enable Single Character Input */
1, /* Read USART Status Register */
1, /* Read Error Code */
0,
2, /* Single Character Output */
2, /* Write USART Command Register */
1, /* Disable Single Character Input */
5, /* Configuration Command */
6, /* Block Input */
0,
1, /* Abort Input */
1, /* Suspend Output */
6, /* Block Output */
1, /* Resume Output */
1, /* Abort Output */
0
};
static char configlen[16] = { /* number of bytes in configure commands */
5, /* Configure Asynchronous */
8, /* Configure Synchronous */
0,
0,
3, /* Configure Input */
5, /* Termination Mask */
3, /* Configure Output */
3, /* Modem Status */
5, /* Buffer Sizes */
2, /* Configure Timer */
0,
0,
0,
0,
0,
0
};
static char resplen[16] = { /* number of bytes in responses */
3, /* Enable Single Character Input */
2, /* Read USART Status Register */
2, /* Read Error Code */
1,
1, /* Single Character Output */
1, /* Write USART Command Register */
1, /* Disable Single Character Input */
2, /* Configuration Command */
6, /* Block Input */
1,
1, /* Abort Input */
1, /* Suspend Output */
5, /* Block Output */
1, /* Resume Output */
1, /* Abort Output */
1
};
#ifndef PORTSELECTOR
#define ISPEED B9600
#else
#define ISPEED B4800
#endif
#define IFLAGS (CS7|CREAD|PARENB)
int mtiticks = 1; /* polling frequency */
int mtisoftdtr = 0; /* if nonzero, softcarrier raises dtr */
int mtidtrlow = 3; /* hold dtr low almost this long */
int mtib134_weird = 0; /* if set, old B134 behavior */
/*
* Should be "void", not "int" - they return no values!
*/
static int mtiopen(/*queue_t *q, int dev, int flag, int sflag*/);
static int mticlose(/*queue_t *q, int flag*/);
static int mtiwput(/*queue_t *q, mblk_t *mp*/);
static struct module_info mtim_info = {
0,
"mti",
0,
INFPSZ,
2048,
128
};
static struct qinit mtirinit = {
putq,
NULL,
mtiopen,
mticlose,
NULL,
&mtim_info,
NULL
};
static struct qinit mtiwinit = {
mtiwput,
NULL,
NULL,
NULL,
NULL,
&mtim_info,
NULL
};
static char *mtimodlist[] = {
"ldterm",
"ttcompat",
NULL
};
struct streamtab mtistab = {
&mtirinit,
&mtiwinit,
NULL,
NULL,
mtimodlist
};
static int mtireioctl(/*long unit*/);
static void mtiioctl(/*struct mtiline *mtp, queue_t *q, mblk_t *mp*/);
static int dmtomti(/*int bits*/);
static int mtitodm(/*int bits*/);
static void mtiparam(/*struct mtiline *mtp*/);
static int mtirstrt(/*struct mtiline *mtp*/);
static void mtistart(/*struct mtiline *mtp*/);
static void bcopy_swab(/*caddr_t pf, caddr_t pt, int n*/);
static int mtimctl(/*struct mtiline *mtp, int bits, int how*/);
static void mtiresponse(/*int mti, struct mti_softc *msc*/);
static int mtidrainring(/*struct mtiline *mtp*/);
static void mticmd(/*struct mtiline *mtp, ...*/);
extern int setcons(/*dev_t dev, u_short uid, u_short gid*/);
extern void resetcons();
#define BYTESWAP(p) ((caddr_t)((int)(p)^1))
static int junk; /* global to prevent optimization */
static int
mtiprobe(reg)
caddr_t reg;
{
if (peek((short *)reg) < 0)
return (0);
return (sizeof (struct mtireg));
}
static int
mtiattach(md)
register struct mb_device *md;
{
register struct mtiline *mtp = &mtiline[md->md_unit*16];
register struct mtireg *mtiaddr = (struct mtireg *)md->md_addr;
register int line;
register char *p;
/*
* Allocate 16*(MTIBUFSIZE+1) bytes (MTIBUFSIZE+1 bytes for each
* line) in DVMA space for output buffers. The "+1" is for an
* extra byte to hold any flow control character being transmitted.
*/
p = (char *)rmalloc(iopbmap, (long)(16*(MTIBUFSIZE+1)));
for (line = 0; line < 16; line++) {
mtp->mt_flags = 0;
mtp->mt_wbits = MTI_ON;
mtp->mt_buf = p;
mtp->mt_dtrlow = time.tv_sec - mtidtrlow;
p += (MTIBUFSIZE+1);
if (md->md_flags & (1<<line))
mtp->mt_ttycommon.t_flags |= TS_SOFTCAR;
mtp++;
}
mtiaddr->mtiie = MTI_RA; /* enable interrupts on resp avail */
/*
* Scan through the lines again, and fix up the modem control
* signals (i.e. lower DTR, unless softcarrier set and mtisoftdtr on).
* we build up a fake mt_dev number, since mtimctl needs to know
* which board and which line to apply the changes to.
*/
mtp = &mtiline[md->md_unit * 16];
for (line = 0; line < 16; ++line) {
mtp->mt_dev = md->md_unit * 16 + line; /* temporary only */
if (mtisoftdtr && (mtp->mt_ttycommon.t_flags & TS_SOFTCAR))
(void) mtimctl(mtp, MTI_ON, DMSET); /* raise dtr */
else
(void) mtimctl(mtp, MTI_OFF, DMSET); /* drop dtr */
}
}
/*ARGSUSED*/
static int
mtiopen(q, dev, flag, sflag)
register queue_t *q;
int dev, flag, sflag;
{
register struct mtiline *mtp;
register int unit, s;
unit = UNIT(dev);
if (unit >= nmti)
return (OPENFAIL);
mtp = &mtiline[unit];
if (mtp->mt_buf == NULL)
return (OPENFAIL);
/*
* Block waiting for carrier to come up, unless this is a no-delay
* open.
*/
s = spltty();
again:
mtp->mt_flags |= MTS_WOPEN;
if (!(mtp->mt_flags & MTS_ISOPEN)) {
/* clear any stale input */
MTI_RING_INIT (mtp);
mtp->mt_ttycommon.t_iflag = 0;
mtp->mt_ttycommon.t_cflag = (ISPEED << IBSHIFT)|ISPEED|IFLAGS;
mtp->mt_ttycommon.t_iocpending = NULL;
mtp->mt_ttycommon.t_size.ws_row = 0;
mtp->mt_ttycommon.t_size.ws_col = 0;
mtp->mt_ttycommon.t_size.ws_xpixel = 0;
mtp->mt_ttycommon.t_size.ws_ypixel = 0;
mtp->mt_wbufcid = 0;
mtp->mt_dev = UNIT(dev); /* needed for mticmd */
mtiparam(mtp);
} else if (mtp->mt_ttycommon.t_flags & TS_XCLUDE && u.u_uid != 0) {
(void) splx(s);
u.u_error = EBUSY;
return (OPENFAIL);
} else if ((dev & OUTLINE) && !(mtp->mt_flags & MTS_OUT)) {
(void) splx(s);
u.u_error = EBUSY;
return (OPENFAIL);
}
(void) mtimctl(mtp, MTI_ON, DMSET);
if (dev & OUTLINE)
mtp->mt_flags |= MTS_OUT;
/*
* Check carrier.
*/
if (mtp->mt_ttycommon.t_flags & TS_SOFTCAR
|| mtimctl(mtp, 0, DMGET) & MTI_SR_DSR)
mtp->mt_flags |= MTS_CARR_ON;
/*
* Unless DTR is held high by softcarrier, set HUPCL.
*/
if ((mtp->mt_ttycommon.t_flags & TS_SOFTCAR) == 0)
mtp->mt_ttycommon.t_cflag |= HUPCL;
/*
* If FNDELAY clear, block until carrier up. Quit on interrupt.
*/
if (!(flag & (FNDELAY|FNBIO|FNONBIO))
&& !(mtp->mt_ttycommon.t_cflag&CLOCAL)) {
if (!(mtp->mt_flags & (MTS_CARR_ON|MTS_OUT))
|| (mtp->mt_flags&MTS_OUT && !(dev&OUTLINE))) {
mtp->mt_flags |= MTS_WOPEN;
if (sleep((caddr_t)&mtp->mt_flags, STIPRI|PCATCH)) {
mtp->mt_flags &= ~MTS_WOPEN;
(void) mtimctl(mtp, MTI_OFF, DMSET);
u.u_error = EINTR;
(void) splx(s);
return (OPENFAIL);
}
goto again;
}
} else {
if (mtp->mt_flags&MTS_OUT && !(dev&OUTLINE)) {
(void) splx(s);
u.u_error = EBUSY;
return (OPENFAIL);
}
}
(void) splx(s);
mtp->mt_ttycommon.t_readq = q;
mtp->mt_ttycommon.t_writeq = WR(q);
q->q_ptr = WR(q)->q_ptr = (caddr_t)mtp;
mtp->mt_flags &= ~MTS_WOPEN;
mtp->mt_flags |= MTS_ISOPEN;
return (dev);
}
/*ARGSUSED*/
static int
mticlose(q, flag)
register queue_t *q;
int flag;
{
register struct mtiline *mtp;
register int s;
if ((mtp = (struct mtiline *)q->q_ptr) == NULL)
return; /* already been closed once */
if (consdev == mtp->mt_dev)
resetcons();
s = spltty();
/*
* wait here until all data is gone or we are interrupted.
*/
while ((mtp->mt_flags & MTS_CARR_ON) &&
((WR(q)->q_count != 0) || (mtp->mt_flags & MTS_BUSY)))
if (sleep ((caddr_t)&lbolt, STOPRI|PCATCH))
break;
if (mtp->mt_flags & MTS_BUSY)
mticmd(mtp, MTI_ABORTOUT);
/*
* If break is in progress, stop it.
*/
(void) mtimctl(mtp, MTI_CR_BREAK, DMBIC);
mtp->mt_flags &= ~MTS_BREAK;
/*
* If line isn't completely opened, or has HUPCL set,
* or has changed the status of TS_SOFTCAR,
* fix up the modem lines.
*/
if (((mtp->mt_flags & (MTS_WOPEN|MTS_ISOPEN)) != MTS_ISOPEN) ||
(mtp->mt_ttycommon.t_cflag & HUPCL) ||
(mtp->mt_flags & MTS_SOFTC_ATTN)) {
/*
* If DTR is being held high by softcarrier,
* set up the MTI_ON set; if not, hang up.
*/
if (mtp->mt_ttycommon.t_flags & TS_SOFTCAR)
(void) mtimctl(mtp, MTI_ON, DMSET);
else
(void) mtimctl(mtp, MTI_OFF, DMSET);
/*
* Don't let an interrupt in the middle of close
* bounce us back to the top; just continue
* closing as if nothing had happened.
*/
if (sleep((caddr_t)&lbolt, STOPRI|PCATCH))
goto out;
}
/*
* If nobody's now using it, turn off receiver interrupts.
*/
if ((mtp->mt_flags & (MTS_ISOPEN|MTS_WOPEN)) == 0)
mticmd(mtp, MTI_DSCI);
out:
/*
* Clear out device state.
*/
if (mtp->mt_flags & MTS_SUSPD)
mticmd(mtp, MTI_RESUME);
mtp->mt_flags = 0;
ttycommon_close(&mtp->mt_ttycommon);
/*
* Cancel outstanding "bufcall" request.
*/
if (mtp->mt_wbufcid) {
unbufcall(mtp->mt_wbufcid);
mtp->mt_wbufcid = 0;
}
q->q_ptr = WR(q)->q_ptr = NULL;
wakeup((caddr_t)&mtp->mt_flags);
(void) splx(s);
}
/*
* Put procedure for write queue.
* Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
* set the flow control character for M_STOPI and M_STARTI messages;
* queue up M_BREAK, M_DELAY, and M_DATA messages for processing
* by the start routine, and then call the start routine; discard
* everything else.
*/
static int
mtiwput(q, mp)
register queue_t *q;
register mblk_t *mp;
{
register struct mtiline *mtp;
int s, c;
mtp = (struct mtiline *)q->q_ptr;
switch (mp->b_datap->db_type) {
case M_STOP:
s = spltty();
if (!(mtp->mt_flags & MTS_STOPPED)) {
mtp->mt_flags |= MTS_STOPPED;
/*
* If an output operation is in progress,
* suspend it.
*/
if (mtp->mt_flags & MTS_BUSY)
mticmd(mtp, MTI_SUSPEND);
}
(void) splx(s);
freemsg(mp);
break;
case M_START:
s = spltty();
if (mtp->mt_flags & MTS_STOPPED) {
mtp->mt_flags &= ~MTS_STOPPED;
/*
* If an output operation is in progress,
* resume it. Otherwise, prod the start
* routine.
*/
if (mtp->mt_flags & MTS_BUSY)
mticmd(mtp, MTI_RESUME);
else
mtistart(mtp);
}
(void) splx(s);
freemsg(mp);
break;
case M_IOCTL:
switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
case TCSBRK:
/*
* The changes do not take effect until all
* output queued before them is drained.
* Put this message on the queue, so that
* "mtistart" will see it when it's done
* with the output before it. Poke the
* start routine, just in case.
*/
putq(q, mp);
mtistart(mtp);
break;
default:
/*
* Do it now.
*/
mtiioctl(mtp, q, mp);
break;
}
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW) {
s = spltty();
/*
* Abort any output in progress.
*/
if (mtp->mt_flags & MTS_BUSY) {
mticmd(mtp, MTI_ABORTOUT);
mtp->mt_flags |= MTS_FLUSH;
}
/*
* Flush our write queue.
*/
flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */
(void) splx(s);
*mp->b_rptr &= ~FLUSHW; /* it has been flushed */
}
if (*mp->b_rptr & FLUSHR) {
s = spltty();
flushq(RD(q), FLUSHDATA);
(void) splx(s);
qreply(q, mp); /* give the read queues a crack at it */
} else
freemsg(mp);
/*
* We must make sure we process messages that survive the
* write-side flush. Without this call, the close protocol
* with ldterm can hang forever. (ldterm will have sent us a
* TCSBRK ioctl that it expects a response to.)
*/
mtistart(mtp);
break;
case M_STOPI:
s = spltty();
if (c = mtp->mt_ttycommon.t_stopc) {
mtp->mt_flowc = c;
/*
* Abort any output in progress, so that the stop character
* comes out NOW (even if output is frozen).
*/
if (mtp->mt_flags & MTS_BUSY)
mticmd(mtp, MTI_ABORTOUT);
mtistart(mtp); /* poke the start routine */
}
(void) splx(s);
freemsg(mp);
break;
case M_STARTI:
s = spltty();
if (c = mtp->mt_ttycommon.t_startc) {
mtp->mt_flowc = c;
/*
* Abort any output in progress, so that the stop character
* comes out NOW (even if output is frozen).
*/
if (mtp->mt_flags & MTS_BUSY)
mticmd(mtp, MTI_ABORTOUT);
mtistart(mtp); /* poke the start routine */
}
(void) splx(s);
freemsg(mp);
break;
case M_BREAK:
case M_DELAY:
case M_DATA:
/*
* Queue the message up to be transmitted,
* and poke the start routine.
*/
putq(q, mp);
mtistart(mtp);
break;
default:
/*
* "No, I don't want a subscription to Chain Store Age,
* thank you anyway."
*/
freemsg(mp);
break;
}
}
/*
* Retry an "ioctl", now that "bufcall" claims we may be able to allocate
* the buffer we need.
*/
static int
mtireioctl(unit)
long unit;
{
register struct mtiline *mtp = &mtiline[unit];
queue_t *q;
register mblk_t *mp;
/*
* The bufcall is no longer pending.
*/
mtp->mt_wbufcid = 0;
if ((q = mtp->mt_ttycommon.t_writeq) == NULL)
return;
if ((mp = mtp->mt_ttycommon.t_iocpending) != NULL) {
mtp->mt_ttycommon.t_iocpending = NULL; /* not pending any more */
mtiioctl(mtp, q, mp);
}
}
/*
* Process an "ioctl" message sent down to us.
*/
static void
mtiioctl(mtp, q, mp)
struct mtiline *mtp;
queue_t *q;
register mblk_t *mp;
{
register struct iocblk *iocp;
register unsigned datasize;
int error;
int s;
iocp = (struct iocblk *)mp->b_rptr;
/*
* The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
* requires a response containing data to be returned to the user,
* and no mblk could be allocated for the data.
* No such "ioctl" alters our state. Thus, we always go ahead and
* do any state-changes the "ioctl" calls for. If we couldn't allocate
* the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
* we just call "bufcall" to request that we be called back when we
* stand a better chance of allocating the data.
*/
if ((datasize =
ttycommon_ioctl(&mtp->mt_ttycommon, q, mp, &error)) != 0) {
if (mtp->mt_wbufcid)
unbufcall(mtp->mt_wbufcid);
mtp->mt_wbufcid = bufcall(datasize, BPRI_HI, mtireioctl,
(long)mtp->mt_dev);
return;
}
if (error == 0) {
/*
* "ttycommon_ioctl" did most of the work; we just use the
* data it set up.
*/
switch (iocp->ioc_cmd) {
case TCSETS:
case TCSETSW:
case TCSETSF:
case TCSETA:
case TCSETAW:
case TCSETAF:
mtiparam(mtp);
break;
case TIOCSSOFTCAR:
mtp->mt_flags |= MTS_SOFTC_ATTN;
break;
}
} else if (error < 0) {
/*
* "ttycommon_ioctl" didn't do anything; we process it here.
*/
error = 0;
switch (iocp->ioc_cmd) {
case TIOCCONS:
error = setcons (mtp->mt_dev, iocp->ioc_uid,
iocp->ioc_gid);
break;
case TCSBRK: {
if (*(int *)mp->b_cont->b_rptr == 0) {
s = spltty();
/*
* Set the break bit, and arrange for
* "mtirstrt" to be called in 1/4 second; it
* will turn the break bit off, and call
* "mtistart" to grab the next message.
*/
(void) mtimctl(mtp, MTI_CR_BREAK, DMBIS);
timeout(mtirstrt, (caddr_t)mtp, hz/4);
mtp->mt_flags |= MTS_BREAK;
(void) splx(s);
}
break;
}
case TIOCSBRK:
(void) mtimctl(mtp, MTI_CR_BREAK, DMBIS);
break;
case TIOCCBRK:
(void) mtimctl(mtp, MTI_CR_BREAK, DMBIC);
break;
case TIOCMSET:
(void) mtimctl(mtp,
dmtomti(*(int *)mp->b_cont->b_rptr), DMSET);
break;
case TIOCMBIS:
(void) mtimctl(mtp,
dmtomti(*(int *)mp->b_cont->b_rptr), DMBIS);
break;
case TIOCMBIC:
(void) mtimctl(mtp,
dmtomti(*(int *)mp->b_cont->b_rptr), DMBIC);
break;
case TIOCMGET:
*(int *)mp->b_cont->b_rptr =
mtitodm(mtimctl(mtp, 0, DMGET));
break;
default:
/*
* We don't understand it either.
*/
error = ENOTTY;
break;
}
}
if (error != 0) {
iocp->ioc_error = error;
mp->b_datap->db_type = M_IOCNAK;
}
qreply(q, mp);
}
static int
dmtomti(bits)
register int bits;
{
register int b = 0;
if (bits & TIOCM_DTR)
b |= MTI_CR_DTR;
if (bits & TIOCM_CAR)
b |= MTI_SR_DSR;
if (bits & TIOCM_RTS)
b |= MTI_CR_RTS;
if (bits & TIOCM_DSR)
b |= MTI_SR_DSR;
return (b);
}
static int
mtitodm(bits)
register int bits;
{
register int b;
b = 0;
if (bits & MTI_CR_DTR)
b |= TIOCM_DTR;
if (bits & MTI_SR_DSR)
b |= TIOCM_CAR|TIOCM_DSR;
if (bits & MTI_CR_RTS)
b |= TIOCM_RTS;
return (b);
}
/*
* Set the parameters of the line based on the values of the "c_iflag"
* and "c_cflag" fields supplied to us.
*/
static void
mtiparam(mtp)
register struct mtiline *mtp;
{
register int mr1, mr2, cr;
register int baudrate;
int s = spltty();
if ((baudrate = mtp->mt_ttycommon.t_cflag&CBAUD) == 0) {
(void) mtimctl(mtp, MTI_OFF, DMSET); /* hang up */
(void) splx(s);
return;
}
mr1 = MTI_MR1_X1_CLK;
mr2 = mti_speeds[baudrate] | MTI_MR2_INIT;
if (mtib134_weird && baudrate == B134) {
/*
* XXX - should B134 set all this stuff in the compatibility
* module, leaving this stuff fairly clean?
*/
mr1 |= MTI_MR1_BITS6|MTI_MR1_PENABLE|MTI_MR1_EPAR|MTI_MR1_1_5STOP;
} else {
switch (mtp->mt_ttycommon.t_cflag&CSIZE) {
case CS5:
mr1 |= MTI_MR1_BITS5;
break;
case CS6:
mr1 |= MTI_MR1_BITS6;
break;
case CS7:
mr1 |= MTI_MR1_BITS7;
break;
case CS8:
mr1 |= MTI_MR1_BITS8;
break;
}
if (mtp->mt_ttycommon.t_cflag&PARENB) {
mr1 |= MTI_MR1_PENABLE;
if (!(mtp->mt_ttycommon.t_cflag&PARODD))
mr1 |= MTI_MR1_EPAR;
}
mr1 |= (mtp->mt_ttycommon.t_cflag&CSTOPB) ? MTI_MR1_2STOP
: MTI_MR1_1STOP;
}
cr = MTI_CR_TXEN | mtp->mt_wbits;
if (mtp->mt_ttycommon.t_cflag&CREAD)
cr |= MTI_CR_RXEN;
mticmd(mtp, MTI_CONFIG, MTIC_ASYNC, mr1, mr2, cr);
mticmd(mtp, MTI_ESCI);
mticmd(mtp, MTI_CONFIG, MTIC_MODEM,
(mtp->mt_ttycommon.t_cflag & CLOCAL) ? 0 : 1);
mticmd(mtp, MTI_CONFIG, MTIC_OUTPUT, 1);
mticmd(mtp, MTI_RSTAT);
(void) splx(s);
}
/*
* Restart output on a line after a delay or break timer expired.
*/
static int
mtirstrt(mtp)
register struct mtiline *mtp;
{
/*
* If break timer expired, turn off the break bit.
*/
if (mtp->mt_flags & MTS_BREAK)
(void) mtimctl(mtp, MTI_CR_BREAK, DMBIC);
mtp->mt_flags &= ~(MTS_DELAY|MTS_BREAK);
mtistart(mtp);
}
/*
* Start output on a line, unless it's busy, frozen, or otherwise.
*/
static void
mtistart(mtp)
register struct mtiline *mtp;
{
register int n, cc;
register queue_t *q;
register mblk_t *bp;
mblk_t *nbp;
register char *current_position;
register int bytes_left;
int s;
s = spltty();
/*
* If the board is busy (i.e., we're waiting for a break timeout
* to expire, or for the current transmission to finish), don't
* grab anything new.
*/
if (mtp->mt_flags & (MTS_BREAK|MTS_BUSY|MTS_FCXMIT))
goto out;
/*
* If we have a flow-control character to transmit, do it now.
* Do so by stuffing the character into the extra byte on the
* end of the DVMA-space buffer. (We can't use MTI_OUT because
* that command, amazingly enough, gives no response to indicate
* when it's done!)
*/
if (mtp->mt_flowc != '\0') {
current_position = &mtp->mt_buf[MTIBUFSIZE];
*BYTESWAP(current_position) = mtp->mt_flowc;
n = current_position - DVMA;
mticmd(mtp, MTI_BLKOUT, n&0xff, (n>>8)&0xff, (n>>16)&0xff,
1, 0);
mtp->mt_flags |= MTS_FCXMIT;
goto out; /* wait for this to finish */
}
/*
* If we're waiting for a delay timeout to expire, don't grab
* anything new.
*/
if (mtp->mt_flags & MTS_DELAY)
goto out;
/*
* If a DMA operation was terminated early, restart it unless
* output is stopped.
*/
if (mtp->mt_dmacount != 0) {
if (!(mtp->mt_flags & MTS_STOPPED)) {
n = mtp->mt_buf - DVMA; /* address of buffer in DVMA space */
n += mtp->mt_dmaoffs; /* advance in Multibus space */
mticmd(mtp, MTI_BLKOUT, n&0xff, (n>>8)&0xff,
(n>>16)&0xff, mtp->mt_dmacount, 0);
mtp->mt_flags |= MTS_BUSY;
}
goto out;
}
if ((q = mtp->mt_ttycommon.t_writeq) == NULL)
goto out; /* not attached to a stream */
/*
* Set up to copy up to MTIBUFSIZE bytes into our (IOPB-space)
* buffer.
*/
current_position = &mtp->mt_buf[0];
bytes_left = MTIBUFSIZE;
while ((bp = getq(q)) != NULL) {
/*
* We have a new message to work on.
* Check whether it's a break, a delay, or an ioctl (the latter
* occurs if the ioctl in question was waiting for the output
* to drain). If it's one of those, process it immediately.
*/
switch (bp->b_datap->db_type) {
case M_BREAK:
if (bytes_left != MTIBUFSIZE) {
putbq(q, bp);
goto transmit;
}
/*
* Set the break bit, and arrange for "mtirstrt"
* to be called in 1/4 second; it will turn the
* break bit off, and call "mtistart" to grab
* the next message.
*/
(void) mtimctl(mtp, MTI_CR_BREAK, DMBIS);
timeout(mtirstrt, (caddr_t)mtp, hz/4); /* 0.25 seconds */
mtp->mt_flags |= MTS_BREAK;
freemsg(bp);
goto out; /* wait for this to finish */
case M_DELAY:
if (bytes_left != MTIBUFSIZE) {
putbq(q, bp);
goto transmit;
}
/*
* Arrange for "mtirstrt" to be called when the
* delay expires; it will turn MTS_DELAY off,
* and call "mtistart" to grab the next message.
*/
timeout(mtirstrt, (caddr_t)mtp,
(int)(*(unsigned char *)bp->b_rptr + 6));
mtp->mt_flags |= MTS_DELAY;
freemsg(bp);
goto out; /* wait for this to finish */
case M_IOCTL:
if (bytes_left != MTIBUFSIZE) {
putbq(q, bp);
goto transmit;
}
/*
* This ioctl was waiting for the output ahead of
* it to drain; obviously, it has. Do it, and
* then grab the next message after it.
*/
mtiioctl(mtp, q, bp);
continue;
}
/*
* We have data to transmit. If output is stopped, put
* it back and try again later.
*/
if (mtp->mt_flags & MTS_STOPPED) {
putbq(q, bp);
goto out;
}
do {
while ((cc = bp->b_wptr - bp->b_rptr) != 0) {
if (bytes_left == 0) {
/*
* Out of buffer space; put this
* buffer back on the queue, and
* transmit what we have.
*/
putbq(q, bp);
goto transmit;
}
if (cc > bytes_left) cc = bytes_left;
/*
* Swap bytes as we copy - little-endian
* Multibus, big-endian processor.
*/
bcopy_swab((caddr_t)bp->b_rptr,
(caddr_t)current_position, cc);
current_position += cc;
bytes_left -= cc;
bp->b_rptr += cc;
}
nbp = bp;
bp = bp->b_cont;
freeb(nbp);
} while (bp != NULL);
}
transmit:
if ((cc = MTIBUFSIZE - bytes_left) != 0) {
n = mtp->mt_buf - DVMA; /* address of buffer in DVMA space */
mticmd(mtp, MTI_BLKOUT, n&0xff, (n>>8)&0xff, (n>>16)&0xff,
cc, 0);
mtp->mt_dmaoffs = 0; /* starting at beginning */
mtp->mt_dmacount = cc; /* transmitting this many characters */
mtp->mt_flags |= MTS_BUSY;
}
out:
(void) splx(s);
}
/*
* Copy bytes and swap them.
*/
static void
bcopy_swab(pf, pt, n)
register caddr_t pf, pt;
register int n;
{
while (n-- > 0)
*BYTESWAP(pt++) = *pf++;
}
/*
* Set or get the modem control status.
* WARNING: may sleep if trying to raise DTR.
*/
static int
mtimctl(mtp, bits, how)
register struct mtiline *mtp;
int bits, how;
{
register int mbits, obits, cr, s, now, held;
again:
s = spltty();
mbits = mtp->mt_wbits | mtp->mt_rbits;
obits = mbits;
switch (how) {
case DMSET:
mbits = bits;
break;
case DMBIS:
mbits |= bits;
break;
case DMBIC:
mbits &= ~bits;
break;
case DMGET:
(void) splx(s);
return (mbits);
}
now = time.tv_sec;
held = now - mtp->mt_dtrlow;
/* if DTR is going low, stash current time away */
if (~mbits & obits & MTI_CR_DTR)
mtp->mt_dtrlow = now;
/* if DTR is going high, sleep until it has been low a bit */
if ((mbits & ~obits & MTI_CR_DTR) && (held < mtidtrlow)) {
(void) splx(s);
(void) sleep((caddr_t)&lbolt, PZERO-1);
goto again;
}
mtp->mt_wbits = mbits&(MTI_CR_DTR|MTI_CR_RTS|MTI_CR_BREAK);
cr = MTI_CR_TXEN | mtp->mt_wbits;
if (mtp->mt_ttycommon.t_cflag&CREAD)
cr |= MTI_CR_RXEN;
mticmd(mtp, MTI_WCMD, cr);
(void) splx(s);
return (mbits);
}
/*
* Interrupt service routine.
* Poll all the devices marked as "alive".
*/
int
mtiintr()
{
register int mti;
register struct mb_device *mtip;
register struct mtireg *mtiaddr;
register struct mti_softc *msc;
int serviced = 0;
for (mti = 0; mti < nmti>>4; mti++) {
if ((mtip = mtiinfo[mti]) == NULL)
continue; /* it wasn't found by autoconfig */
mtiaddr = (struct mtireg *)mtip->md_addr;
msc = &mti_softc[mti];
/*
* If command FIFO is ready, see if we have any
* queued commands to send out.
*/
if (mtiaddr->mtistat&MTI_READY) {
register struct clist *q;
register int n;
q = &msc->msc_cmdq;
if ((n = getc(q)) > 0) {
while (n--)
mtiaddr->mticmd = getc(q);
mtiaddr->mtigo = 1;
serviced++;
if (q->c_cc == 0)
mtiaddr->mtiie = MTI_RA;
} else
mtiaddr->mtiie = MTI_RA;
}
if (mtiaddr->mtistat&MTI_RA) {
register int have;
register u_char *p, *p0;
serviced++;
/*
* Determine if we have an entire response yet.
* If not, keep accumulating response bytes.
*/
have = msc->msc_have;
p0 = msc->msc_rbuf;
p = p0 + have;
while (mtiaddr->mtistat & MTI_VD) {
*p++ = mtiaddr->mtiresp;
if (++have >= resplen[*p0>>4]) {
mtiresponse(mti, msc);
junk = mtiaddr->mticra;
have = 0;
p = p0;
}
}
msc->msc_have = have;
}
}
return (serviced);
}
/*
* Process response to a command.
*/
static void
mtiresponse(mti, msc)
int mti;
register struct mti_softc *msc;
{
register struct mtiline *mtp;
register u_char *p = msc->msc_rbuf;
register int cmd, s, c, svpri, line;
int overrun = 0;
int ringfull = 0;
register queue_t *q;
cmd = *p++;
line = cmd & 15;
mtp = &mtiline[(mti<<4)+line];
cmd &= 0xf0;
/*
* First, read and handle the UART status
* byte for commands that return it.
*/
switch (cmd) {
case MTI_ESCI:
case MTI_RSTAT:
case MTI_CONFIG:
case MTI_BLKOUT:
s = *p++;
if ((s & MTI_SR_DSR) ||
(mtp->mt_ttycommon.t_flags & TS_SOFTCAR)) {
/* carrier present */
if ((mtp->mt_flags & MTS_CARR_ON) == 0) {
mtp->mt_flags |= MTS_CARR_ON;
if ((q = mtp->mt_ttycommon.t_readq) != NULL)
(void) putctl(q->q_next, M_UNHANGUP);
wakeup((caddr_t)&mtp->mt_flags);
}
} else {
if ((mtp->mt_flags&MTS_CARR_ON) &&
!(mtp->mt_ttycommon.t_cflag&CLOCAL)) {
/*
* Carrier went away.
* Drop DTR, abort any output in progress,
* indicate that output is not stopped, and
* send a hangup notification upstream.
*/
(void) mtimctl(mtp, MTI_CR_DTR, DMBIC);
svpri = spltty();
if (mtp->mt_flags & MTS_BUSY) {
mticmd(mtp, MTI_ABORTOUT);
mtp->mt_flags |= MTS_FLUSH;
}
mtp->mt_flags &= ~MTS_STOPPED;
(void) splx(svpri);
if ((q = mtp->mt_ttycommon.t_readq) != NULL)
(void) putctl(q->q_next, M_HANGUP);
}
mtp->mt_flags &= ~MTS_CARR_ON;
}
mtp->mt_rbits = s&(MTI_SR_DCD|MTI_SR_DSR);
break;
}
/*
* Finish processing the command.
*/
switch (cmd) {
case MTI_ESCI:
c = *p++;
if ((mtp->mt_flags & MTS_ISOPEN) == 0) {
#if 0
/*
* What does this do?
*/
wakeup((caddr_t)&tp->t_rawq);
#endif
#ifdef PORTSELECTOR
if ((mtp->mt_flags&MTS_WOPEN) == 0)
#endif
break;
}
if ((q = mtp->mt_ttycommon.t_readq) == NULL)
break; /* nobody listening */
if (s&MTI_SR_DO)
++overrun;
if (s&MTI_SR_FE) {
if (c == 0) {
(void) putctl(q->q_next, M_BREAK);
break; /* throw NUL away */
} else
s |= MTI_SR_PE; /* treat it like parity error */
}
if (s&MTI_SR_PE && (mtp->mt_ttycommon.t_iflag & INPCK)) {
/*
* IGNPAR PARMRK RESULT
* off off 0
* off on 3 byte sequence
* on either ignored
*/
if (!(mtp->mt_ttycommon.t_iflag & IGNPAR)) {
/*
* The receive interrupt routine has already
* stuffed c into the ring. Dig it out again,
* since the current mode settings don't allow
* it to appear in that position (it needs the
* 0377, 0 stuff first).
*/
if (MTI_RING_CNT(mtp) != 0)
MTI_RING_UNPUT(mtp);
else
log(LOG_ERR,
"mti%d%x: parity error ignored\n",
mti, line);
if (mtp->mt_ttycommon.t_iflag & PARMRK) {
if (MTI_RING_POK(mtp, 3)) {
MTI_RING_PUT(mtp, 0377);
MTI_RING_PUT(mtp, 0);
MTI_RING_PUT(mtp, c);
} else
++ringfull;
} else {
if (MTI_RING_POK(mtp, 1))
MTI_RING_PUT(mtp, 0);
else
++ringfull;
}
} else {
if (MTI_RING_CNT(mtp) != 0)
MTI_RING_UNPUT(mtp);
else
log(LOG_ERR,
"mti%d%x: parity error went up\n",
mti, line);
}
} else {
if (MTI_RING_POK(mtp, 1)) {
if (c == 0377 &&
((mtp->mt_ttycommon.t_iflag &
(PARMRK | IGNPAR | ISTRIP)) == PARMRK)) {
if (MTI_RING_POK(mtp, 2)) {
MTI_RING_PUT(mtp, 0377);
MTI_RING_PUT(mtp, c);
} else
++ringfull;
} else
MTI_RING_PUT(mtp, c);
} else
++ringfull;
}
/*
* If a "stop input" character arrived, or the silo is at
* least half full, drain it.
*/
if (MTI_RING_FRAC(mtp) || ((c & 0177) == mtp->mt_ttycommon.t_stopc))
mtidrainring(mtp);
else {
if (!(mtp->mt_flags & MTS_DRAINPEND)) {
mtp->mt_flags |= MTS_DRAINPEND;
timeout(mtidrainring, (caddr_t) mtp, mtiticks);
}
}
break;
case MTI_RERR:
c = *p++;
log(LOG_ERR, "mti%d%x: read error code <%x>. Probable hardware fault\n",
mti, line, c);
break;
case MTI_BLKOUT:
s = *p++; /* get termination cause */
c = *p++; /* get number of characters transferred */
c |= *p++ << 8;
if (mtp->mt_flags & MTS_FCXMIT) {
if (s & 4)
log(LOG_ERR, "mti%d%x: DMA error after %d FLOW bytes (at %x)\n",
mti, line, c, mtp->mt_buf + MTIBUFSIZE + c);
mtp->mt_flags &= ~MTS_FCXMIT;
if (c > 0)
mtp->mt_flowc = '\0';
} else {
if (s & 4)
log(LOG_ERR, "mti%d%x: DMA output error after %d of %d bytes (at %x)\n",
mti, line, c, mtp->mt_dmacount,
mtp->mt_buf + mtp->mt_dmaoffs + c);
if (mtp->mt_flags & MTS_FLUSH) {
/*
* Discard untransmitted output.
*/
mtp->mt_dmacount = 0;
} else {
mtp->mt_dmacount -= c;
mtp->mt_dmaoffs += c;
}
mtp->mt_flags &= ~(MTS_BUSY|MTS_FLUSH);
}
mtistart(mtp);
break;
case MTI_RSTAT:
case MTI_CONFIG:
break;
case MTI_BLKIN:
default:
log(LOG_ERR, "mti%d%x: impossible response %x\n", mti, line,
*msc->msc_rbuf);
}
if (overrun)
log(LOG_WARNING, "mti%d%x: silo overflow\n", mti, line);
if (ringfull)
log(LOG_WARNING, "mti%d%x: ring overflow\n", mti, line);
}
/*
* Drain the ring for an MTI port.
*/
static int
mtidrainring(mtp)
register struct mtiline *mtp;
{
register mblk_t *bp;
register int cc;
register queue_t *q;
int s;
s = splclock();
untimeout(mtidrainring, (caddr_t) mtp);
mtp->mt_flags &= ~MTS_DRAINPEND;
(void) spltty();
if ((q = mtp->mt_ttycommon.t_readq) == NULL) {
MTI_RING_INIT (mtp);
(void) splx(s);
return;
}
if ((cc = MTI_RING_CNT (mtp)) > 0)
do {
if (cc > 16)
cc = 16;
if (((bp = allocb(cc, BPRI_MED)) == NULL) ||
!canput (q->q_next)) {
freemsg(bp);
MTI_RING_INIT(mtp);
ttycommon_qfull(&mtp->mt_ttycommon, q);
break;
}
do {
*(bp->b_wptr++) = MTI_RING_GET(mtp);
} while (--cc > 0);
putnext(q, bp);
} while ((cc = MTI_RING_CNT (mtp)) > 15);
if (MTI_RING_CNT(mtp) != 0) {
if (!(mtp->mt_flags & MTS_DRAINPEND)) {
mtp->mt_flags |= MTS_DRAINPEND;
timeout(mtidrainring, (caddr_t) mtp, mtiticks);
}
}
(void) splx(s);
}
int mticmdwait = 30; /* 30 determined empiricly */
/*
* Send a command to the device.
*/
/*VARARGS1*/
/*ARGSUSED*/
static void
mticmd(mtp, va_alist)
struct mtiline *mtp;
va_dcl
{
int mti = BOARD(mtp->mt_dev);
register struct mtireg *mtiaddr =
(struct mtireg *)mtiinfo[mti]->md_addr;
register int n, useq = 0;
register struct clist *q = &mti_softc[mti].msc_cmdq;
register int cmd, a1;
va_list p;
register int s;
top:
s = spltty();
n = 0;
if (q->c_cc)
useq = 1;
else
while ((mtiaddr->mtistat & MTI_READY) == 0)
if (++n > mticmdwait) {
mtiaddr->mtiie = MTI_RA|MTI_READY;
if (mtiaddr->mtistat & MTI_READY)
mtiaddr->mtiie = MTI_RA;
else
useq = 1;
break;
} else
(void) splx(splx(s));
if (!useq && mtiaddr->mtistat & MTI_ERR) {
mtiaddr->mticmd = MTI_RERR;
mtiaddr->mtigo = 1;
(void) splx(s);
goto top;
}
va_start(p);
cmd = va_arg(p, int);
a1 = va_arg(p, int);
if (cmd == MTI_SUSPEND)
mtp->mt_flags |= MTS_SUSPD;
if (cmd == MTI_RESUME)
mtp->mt_flags &= ~MTS_SUSPD;
if (cmd == MTI_CONFIG)
n = configlen[a1>>4];
else
n = cmdlen[cmd>>4];
cmd |= LINE(mtp->mt_dev);
if (useq) {
(void) putc(n, q);
(void) putc(cmd, q);
if (--n) {
(void) putc(a1, q);
while (--n)
(void) putc(va_arg (p, int), q);
}
} else {
mtiaddr->mticmd = cmd;
if (--n) {
mtiaddr->mticmd = a1;
while (--n)
mtiaddr->mticmd = va_arg(p, int);
}
mtiaddr->mtigo = 1;
}
va_end(p);
(void) splx(s);
}
/*
* Reset state of driver if Multibus reset was necessary.
* Reset parameters and restart transmission on open lines.
*/
int
mtireset()
{
register int unit;
register struct mtiline *mtp;
register struct mb_device *md;
for (unit = 0; unit < nmti; unit++) {
md = mtiinfo[BOARD(unit)];
if (md == 0 || md->md_alive == 0)
continue;
if (LINE(unit) == 0)
printf(" mti%d", BOARD(unit));
mtp = &mtiline[unit];
if (mtp->mt_flags & (MTS_ISOPEN|MTS_WOPEN)) {
mtiparam(mtp);
(void) mtimctl(mtp, 0, DMBIS);
mtp->mt_flags &= ~(MTS_DELAY|MTS_BREAK|MTS_BUSY|MTS_FCXMIT|MTS_DRAINING);
mtistart(mtp);
}
}
}