From: ag@cogsci.Berkeley.EDU (Keith Gabryelski)
Newsgroups: comp.unix.amiga
Subject: Fixes for /dev/term/ql?? and /dev/term/ser garbage.
Date: 8 Nov 1992 18:14:18 GMT
Organization: Institute of Cognitive Studies, U.C. Berkeley
Distribution: usa
NNTP-Posting-Host: cogsci.berkeley.edu

Included in the shell archive below is a new version of `ql.c'
and `sl.c' which had some bugs in them that would cause garbage
to be sent to user land under some circumstances.

To install these new drivers you will need a version of amiga unix
that is greater than 1.1 and the `conf' package installed.

These source files should replace /usr/sys/amiga/driver/ql.c and
/usr/sys/amiga/driver/sl.c.  Please remember to back up the old versions
so that if these drivers turn out to be in worse shape you can back
out this change.

Make and install a new kernel as described in the release notes
and you should be fine.

Thanks to Mike Ditto for bringing the bug to my attention.

Thanks to Rich Skrenta for testing the fixed drivers.

Pax, Keith

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# This archive contains:
#	ql.c	sl.c	
#

echo x - ql.c
cat >ql.c <<'@EOF'
/* mps6551 */
#include "sys/types.h"
#include "sys/param.h"
#include "sys/dir.h"
#include "sys/file.h"
#include "sys/signal.h"
#include "sys/termio.h"
#include "sys/termios.h"
#include "sys/stream.h"
#include "sys/stropts.h"
#include "sys/strtty.h"
#include "sys/errno.h"
#include "sys/conf.h"
#include "sys/sysmacros.h"
#include "sys/inline.h"
#include "sys/systm.h"
#include "sys/cred.h"
#include "ql6502.h"
#include "ql.h"

static int qlopen(struct queue *rq, dev_t *devp, int flag, int sflag,
		  cred_t *credp);
static int qlclose(struct queue *q, int oflag, cred_t *credp);
static void output(ql_t *ql, mblk_t *bp);
static int getoblk(struct strtty *tp);
static void delay(struct strtty *tp);
static void flush(struct strtty *tp, int cmd);
static int qlwput(struct queue *q, mblk_t *bp);
static void qlproc(struct strtty *tp, int cmd);
static void qlputioc(queue_t *q, mblk_t *bp);
static int qlwsrv();
static void input(ql_t *ql);
static int qlrsrv(queue_t *q);
static void handle_exceptions(ql_t *ql);
static void qlparam(ql_t *ql, xmitter xm);
static void qlsrvioc(queue_t *q, mblk_t *mp);
static void ql_ttrstrt(ql_t *ql);
static void newcard(device_t *dp);
static void startcard(device_t *dp);
static void initialize(void);
void qlintr(void);

/*
 * STREAM data structure definitions.
 */

static struct module_info ql_minfo =
{
    0x7777, "ql", 0, INFPSZ, 256, 128,
};

static struct qinit qlrinit =
{
    NULL, qlrsrv, qlopen, qlclose, NULL, &ql_minfo, NULL,
};

static struct qinit qlwinit =
{
    qlwput, qlwsrv, NULL, NULL, NULL, &ql_minfo, NULL,
};

struct streamtab qlinfo =
{
    &qlrinit, &qlwinit, NULL, NULL,
};

struct strtty ql_tty[NCARD][NLINE];
ql_t ql_board[NCARD][NLINE];
device_t *qladdr[NCARD];

static int qlopen(struct queue *rq, dev_t *devp, int flag, int sflag,
		  cred_t *credp)
{
    int s, minor_device;
    ql_t *ql;

    s = spltty();

    initialize();

    if (sflag)
    {
	splx(s);
	return EINVAL;
    }

    minor_device = getminor(*devp);

    if ((qlcard(minor_device) >= NCARD) || (qlline(minor_device) >= NLINE))
    {
	splx(s);
	return ENXIO;
    }

    ql = &ql_board[qlcard(minor_device)][qlline(minor_device)];

    if (!ql->lp)
    {
	splx(s);
	return ENXIO;
    }

    {
	struct stroptions *sop;
	mblk_t *mop = allocb(sizeof (struct stroptions), BPRI_MED);

	if (!mop)
	{
	    splx(s);
	    return EAGAIN;
	}

	mop->b_datap->db_type = M_SETOPTS;
	sop = (struct stroptions *)mop->b_wptr;
	mop->b_wptr += sizeof (struct stroptions);
	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
	sop->so_hiwat = 512;
	sop->so_lowat = 256;

	putnext(rq, mop);
    }

    WR(rq)->q_ptr = rq->q_ptr = (caddr_t)ql;

    if (!(ql->tp->t_state & ISOPEN))
    {
	ql->tp->t_rdqp = rq;
	ql->tp->t_dev = minor_device;
	ql->tp->t_line = 0;
	ql->tp->t_iflag = 0;
	ql->tp->t_oflag = 0;
	ql->tp->t_lflag = 0;
	ql->tp->t_cflag = B9600|SSPEED|CS8|CREAD|HUPCL;

	qlparam(ql, OPEN);
	ql->rts = TRUE;
	ql->rsync = FALSE;
	ql->lp->rsync = TRUE;

	while (!ql->rsync)
	    sleep(ql, TTIPRI);
    }

    if ((flag & (FNDELAY|FNONBLOCK)) || ql->carrier)
    {
	ql->tp->t_state |= CARR_ON;
	if (ql->tp->t_state & WOPEN)
	{
	    ql->tp->t_state &= ~WOPEN;
	    wakeup(ql);
	}
    }
    else
    {
	while (!(ql->tp->t_state & CARR_ON))
	{
	    ql->tp->t_state |= WOPEN;
	    sleep(ql, TTIPRI);
	}
    }

    ql->tp->t_state |= ISOPEN;

    splx(s);

    return 0;
}

static int qlclose(struct queue *q, int oflag, cred_t *credp)
{
    int s = spltty();
    ql_t *ql = (ql_t *)q->q_ptr;
    struct strtty *tp = ql->tp;

    if (tp->t_state & ISOPEN)
    {
	int cflag = tp->t_cflag;

	if (!(oflag & (FNDELAY|FNONBLOCK)))
	{
	    while (WR(q)->q_first || tp->t_state & BUSY)
	    {
		(void) getoblk(tp);

		if (WR(q)->q_first || tp->t_state & BUSY)
		{
		    tp->t_state |= TTIOW;

		    if (sleep((caddr_t)&tp->t_oflag, PZERO+1 | PCATCH))
		    {
			tp->t_state &= ~TTIOW;
			break;
		    }
		}
	    }
	}
    }

    if (ql->tp->t_cflag & HUPCL)
	qlparam(ql, CLOSE);

    tp->t_rdqp = NULL;
    tp->t_state = 0;		/* Just to be really sure --ford */

    splx(s);
}

static void output(ql_t *ql, mblk_t *bp)
{
    struct strtty *tp = ql->tp;
    queue_t *q;

    q = WR(tp->t_rdqp);

    if (ql->lp->thead == ql->lp->ttail)
    {
	tp->t_state &= ~BUSY;
	
	if (tp->t_state & TTIOW)
	{
	    tp->t_state &= ~TTIOW;
	    wakeup(&tp->t_oflag);
	}
    }

#if 0
    if (tp->t_state & BUSY)
	return;
#endif

    if (ql->lp->ttandem)
    {
	putbq(q, bp);
	return;
    }

    if (tp->t_state & TTXON)
    {
	tp->t_state &= ~TTXON;
	ql->lp->ttandem = CSTART;
	putbq(q, bp);
	return;
    }

    if (tp->t_state & TTXOFF)
    {
	tp->t_state &= ~TTXOFF;
	ql->lp->ttandem = CSTOP;
	putbq(q, bp);
	return;
    }

    if ((ql->lp->tflush) || (tp->t_state & TIMEOUT))
    {
	putbq(q, bp);
	return;
    }

    do
    {
	while ((ql->lp->thead+1)%BUFSIZE != ql->lp->ttail &&
	       (bp->b_rptr < bp->b_wptr))
	{
	    ql->tbufp[ql->lp->thead] = *bp->b_rptr++;
	    ql->lp->thead = (ql->lp->thead+1) % BUFSIZE;
	    tp->t_state |= BUSY;
	}

	if (bp->b_rptr < bp->b_wptr)
	{
	    putbq(q, bp);		/* ql buffer full--bail */
	    return;
	}
	else
	{
	    mblk_t *bp1 = bp;

	    bp = unlinkb(bp1);
	    freeb(bp1);
	}
    } while (bp);
}

/*
 * Process an output block.
 * Returns nonzero if there was nothing in the queue.
 */

static int getoblk(struct strtty *tp)
{
    int s;
    struct queue *q;
    ql_t *ql;
    mblk_t *bp;

    s = spltty();

    if (!tp->t_rdqp)
    {
	splx(s);
	return 1;
    }

    q = WR(tp->t_rdqp);
    ql = (ql_t *)q->q_ptr;

    do
    {
	bp = getq(q);

	if (!bp)
	{
	    /* wakeup close write queue drain */
	    if (ql->lp->thead == ql->lp->ttail)
	    {
		tp->t_state &= ~BUSY;

		if (tp->t_state & TTIOW)
		{
		    tp->t_state &= ~TTIOW;
		    wakeup(&tp->t_oflag);
		}
	    }
	    splx(s);
	    return 1;
	}

	switch (bp->b_datap->db_type)
	{
	case M_DATA:
#if 0
	    printf("qlgetoblk 0x%x: got DATA msg, %d bytes (%x %x %x)\n",
		   tp, blklen(bp), bp->b_rptr[0], bp->b_rptr[1],
		   bp->b_rptr[2]);
#endif
	    if (tp->t_state & (TTSTOP | TIMEOUT))
	    {
		putbq(q, bp);
		splx(s);
		return 0;
	    }

	    /*
	     * queue bytes to be sent out
	     */
	    output(ql, bp);
	    break;

	case M_IOCTL:
	case M_IOCDATA:
	    qlsrvioc(q, bp);
	    break;

	default:
	    printf("qlgetoblk 0x%x: got an unknown message, type 0x%x\n",
		   tp, bp->b_datap->db_type);
	    freemsg(bp);
	    break;
	}
    } while (!(tp->t_state & BUSY));

    splx(s);
    return 0;
}

static void delay(struct strtty *tp)
{
    int s = spltty();

    tp->t_state &= ~TIMEOUT;

    splx(s);

    getoblk(tp);
}

static void flush(struct strtty *tp, int cmd)
{
    struct queue *q = WR(tp->t_rdqp);
    ql_t *ql = (ql_t *)q->q_ptr;
    int s = spltty();

    if (cmd&FWRITE)
    {
	flushq(WR(tp->t_rdqp), FLUSHDATA);
	tp->t_state &= ~(BUSY|TBLOCK);
	if (tp->t_state & TTIOW)
	{
	    tp->t_state &= ~TTIOW;
	    wakeup(&tp->t_oflag);
	}

	qlproc(tp, T_WFLUSH);
    }

    if (cmd&FREAD)
    {
	flushq(tp->t_rdqp, FLUSHDATA);
	tp->t_state &= ~(TBLOCK);

	qlproc(tp, T_RFLUSH);
    }

    splx(s);

    getoblk(tp);
}

static int qlwput(struct queue *q, mblk_t *bp)
{
    int s;
    ql_t *ql = (ql_t *)q->q_ptr;
    struct strtty *tp = ql->tp;
    mblk_t *bp1;

    switch (bp->b_datap->db_type)
    {
    case M_DATA:
	if (!(tp->t_state & CARR_ON))
	{
	    putq(q, bp);
	    return 0;
	}

	s = spltty();

	while (bp)
	{
	    bp->b_datap->db_type = M_DATA;
	    bp1 = unlinkb(bp);

	    if (blklen(bp) <= 0)
		freeb(bp);
	    else
		putq(q, bp);

	    bp = bp1;
	}

	splx(s);

	if (q->q_first)
	    getoblk(tp);
	break;

    case M_IOCTL:
    case M_IOCDATA:
	qlputioc(q, bp);
	if (q->q_first)
	    getoblk(tp);
	break;

    case M_CTL:
	{
	    struct iocblk *iocb = (struct iocblk *)bp->b_rptr;

	    if ((blklen(bp) == sizeof *iocb) &&
		(iocb->ioc_cmd == MC_CANONQUERY))
		iocb->ioc_cmd = MC_DO_CANON;
	    else
		bp->b_datap->db_type = M_IOCNAK;

	    putnext(RD(q), bp);
	}
	break;

    case M_FLUSH:
	s = spltty();
	switch (bp->b_rptr[0])
	{
	case FLUSHRW:
	    flush(tp, (FREAD|FWRITE));
	    bp->b_rptr[0] = FLUSHR;
	    putnext(RD(q), bp);
	    break;

	case FLUSHR:
	    flush(tp, FREAD);
	    putnext(RD(q), bp);
	    break;

	case FLUSHW:
	    flush(tp, FWRITE);
	    freemsg(bp);
	    break;
	}
	splx(s);
	break;

    case M_BREAK:
	s = spltty();
	qlparam(ql, SETBREAK);
	tp->t_state |= TIMEOUT;
	splx(s);
	timeout(ql_ttrstrt, ql, HZ/4);
	freemsg(bp);
	break;

    case M_START:
	s = spltty();
	qlproc(tp, T_RESUME);
	splx(s);
	freemsg(bp);
	getoblk(tp);
	break;

    case M_STOP:
	s = spltty();
	qlproc(tp, T_SUSPEND);
	splx(s);
	freemsg(bp);
	break;

    case M_DELAY:
	s = spltty();
	tp->t_state |= TIMEOUT;
	timeout(delay, (caddr_t)tp, bp->b_rptr[0]);
	splx(s);
	freemsg(bp);
	break;

    case M_STARTI:
	/* Can't start/stop keyboard */
	freemsg(bp);
	break;

    case M_STOPI:
	/* Can't start/stop keyboard */
	freemsg(bp);
	break;

    case M_READ:
	/* <EH> Read queue is always up-to-date? */
	freemsg(bp);
	break;

    default:
	printf("qlwput: got an unknown message 0x%x\n", bp->b_datap->db_type);
	freemsg(bp);
	break;
    }
}

/*
 * This does all the work.
 */

static void qlproc(struct strtty *tp, int cmd)
{
    int s;
    ql_t *ql = &ql_board[0][tp-ql_tty[0]];

    switch (cmd)
    {
    case T_TIME:
	s = spltty();
	qlparam(ql, OPEN);
	tp->t_state &= ~TIMEOUT;
	splx(s);
	getoblk(tp);
	break;

    case T_RESUME:		/* resume output */
	s = spltty();
	tp->t_state &= ~TTSTOP;
	ql->lp->tdisable = FALSE;
	splx(s);
	getoblk(tp);
	break;

    case T_SUSPEND:		/* suspend output */
	ql->lp->tdisable = TRUE;
	s = spltty();
	tp->t_state |= TTSTOP;
	splx(s);
	break;

    case T_BREAK:
	qlparam(ql, SETBREAK);
	s = spltty();
	tp->t_state |= TIMEOUT;
	splx(s);
	timeout(ql_ttrstrt, ql, HZ/4);
	getoblk(tp);
	break;

    case T_OUTPUT:
	break;

    case T_BLOCK:
	tp->t_state &= ~TTXON;
	tp->t_state |= TBLOCK | TTXOFF;
	break;

    case T_RFLUSH:
	if (!(tp->t_state&TBLOCK))
	    break;
	/* Fall through case */
    case T_UNBLOCK:
	tp->t_state &= ~ (TTXOFF|TBLOCK);
	tp->t_state |= TTXON;
	break;

    case T_WFLUSH:
	s = spltty();
	ql->lp->tdisable = TRUE;
	ql->lp->tflush = TRUE;
	tp->t_state &= ~TTSTOP;
	splx(s);
	break;
    }
}

static void qlputioc(queue_t *q, mblk_t *bp)
{
    ql_t *ql =  (ql_t *)q->q_ptr;
    struct strtty *tp = ql->tp;
    struct iocblk *iocbp = (struct iocblk *)bp->b_rptr;
    mblk_t *bp1;

    switch (iocbp->ioc_cmd)
    {
    case TCSETA:
    case TCSETS:
	if (tp->t_state & BUSY)
	    putbq(q, bp);		/* queue these for later */
	else
	    qlsrvioc(q, bp);
	return;

    case TCGETA:
    case TCGETS:			/* immediate parm retrieve */
	qlsrvioc(q, bp);
	return;

    default:
	if ((iocbp->ioc_cmd&IOCTYPE) == LDIOC)
	{
	    bp->b_datap->db_type = M_IOCACK; /* ignore LDIOC cmds */
	    freemsg(unlinkb(bp));
	    iocbp->ioc_count = 0;
	    putnext(RD(q), bp);
	    return;
	}
	break;
    }

    /* Other IOCTLs get queued... */
    if (q->q_first || (tp->t_state & BUSY))
	putbq(q, bp);
    else
	qlsrvioc(q, bp);
}

static int qlwsrv()
{
    return 0;
}

static void input(ql_t *ql)
{
    struct strtty *tp = ql->tp;
    queue_t *q = tp->t_rdqp;
    unsigned int i, c;
    unsigned char *p;
    mblk_t *bp;

    if (!(tp->t_state & (WOPEN|ISOPEN)))
	return;

    if (tp->t_state & ISOPEN)
    {
	while ((i = ql->lp->rhead - ql->rtail) != 0)
	{
	    if (i > BUFSIZE)
		i = BUFSIZE - ql->rtail;

	    if (canput(q->q_next))
	    {
		if (!ql->rts)
		{
		    ql->rts = TRUE;
		    qlparam(ql, OPEN);
		}
	    }
	    else
	    {
		ql->rts = FALSE;
		qlparam(ql, RESETRTS);
		break;
	    }

	    bp = allocb(i, BPRI_HI);
	    if (!bp)
		break;

	    p = &ql->rbufp[ql->rtail];
	    ql->rtail = (ql->rtail+i) % BUFSIZE;

	    while (i--)
	    {
		c = *p++;

		if (tp->t_iflag & ISTRIP)
		    c &= 0x7F;

		if (tp->t_iflag & IXON)
		{
		    if (c == CSTART)
		    {
			qlproc(tp, T_RESUME);
			continue;
		    }
		    if (c == CSTOP)
		    {
			qlproc(tp, T_SUSPEND);
			continue;
		    }
		    if ((tp->t_state & TTSTOP) && (tp->t_iflag & IXANY))
			qlproc(tp, T_RESUME);
		}

		*bp->b_wptr++ = c;
	    }

	    putnext(q, bp);
	}
    }
    else
	ql->rtail = ql->lp->rhead;
}

static int qlrsrv(queue_t *q)
{
    input((ql_t *)q->q_ptr);
    return 0;
}

static void handle_exceptions(ql_t *ql)
{
    struct strtty *tp = ql->tp;
    switch (ql->lp->rcondition)
    {
    case BREAK_DETECTED:
	if ((tp->t_state & ISOPEN) && (!(tp->t_iflag&IGNBRK)))
	{
	    if (!(tp->t_lflag & NOFLSH))
		flush(tp, (FREAD|FWRITE));

	    putctl1(tp->t_rdqp->q_next, M_BREAK, 0);

	    if (tp->t_iflag & BRKINT)
		putctl1(tp->t_rdqp->q_next, M_SIG, SIGINT);
	}
	break;

    case CARR_DETECTED:
	ql->carrier = TRUE;
	tp->t_state |= CARR_ON;
	if (tp->t_state & WOPEN)
	{
	    tp->t_state &= ~WOPEN;
	    wakeup(ql);
	}
	break;

    case RECEIVER_SYNC:
	ql->rsync = TRUE;
	wakeup(ql);
	break;

    case CARR_DROPPED:
	ql->carrier = FALSE;

	if (tp->t_state & ISOPEN)
	{
	    if (!(tp->t_cflag & CLOCAL))
	    {
		flush(tp, FREAD|FWRITE);
		tp->t_state &= ~CARR_ON;
		putctl1(tp->t_rdqp->q_next, M_HANGUP, 0);
	    }
	}
	else
	    tp->t_state &= ~CARR_ON;

	break;
    }

    ql->lp->rcondition = 0;
}

static void qlparam(ql_t *ql, xmitter xm)
{
    struct strtty *tp;
    line_t *lp;
    int	b;
    static unsigned char baudtab[] =
    {
	0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x01, 0x06,
	0x07, 0x08, 0x09, 0x0A, 0x0C, 0x0E, 0x0F, 0x00,
    };

    tp = ql->tp;
    lp = ql->lp;
    b = baudtab[tp->t_cflag&CBAUD];

    lp->xon = tp->t_iflag&IXON? TRUE: FALSE;
    lp->control = 0x10 | b;

    switch ((int)xm)
    {
    case OPEN:
	if ((tp->t_cflag&CBAUD) != B0)
	{
	    lp->command = 0x0B;
	    break;
	}
	/* fall through */
    case CLOSE:
	lp->command = 0x00;
	break;

    case SETBREAK:
	lp->command = 0x0D;
	break;

    case RESETRTS:
	lp->command &= ~0xC;	/* Stop sending */
#if 0				/* just to remind me of states that could be */
	lp->command |= 0x8;	/* rts assert= normal state-- start ending */
	lp->command |= 0x4;	/* illeagal  */
	lp->command |= 0xC;	/* rts asserted and break is in action. */
#endif
	break;
    }

    if (tp->t_cflag & PARENB)
    {
	if (tp->t_cflag & PARODD)
	    lp->command |= 0x20;
	else
	    lp->command |= 0x60;
    }

    switch (tp->t_cflag & CSIZE)
    {
    case CS5:
	lp->control |= 0x60;
	break;
    case CS6:
	lp->control |= 0x40;
	break;
    case CS7:
	lp->control |= 0x20;
    }

    lp->setparams = TRUE;
}

static void qlsrvioc(queue_t *q, mblk_t *mp)
{
    ql_t *ql = (ql_t *)q->q_ptr;
    struct strtty *tp = ql->tp;
    struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
    int error;
    mblk_t *bp1;

    if (mp->b_datap->db_type == M_IOCDATA)
    {
	/* For copyin/copyout failures, just free message. */
	if (((struct copyresp *)mp->b_rptr)->cp_rval)
	{
	    freemsg(mp);
	    return;
	}

	if (!((struct copyresp *)mp->b_rptr)->cp_private)
	{
	    mp->b_datap->db_type = M_IOCACK;
	    freemsg(unlinkb(mp));
	    iocbp->ioc_count = 0;
	    iocbp->ioc_rval = 0;
	    iocbp->ioc_error = 0;
	    putnext(RD(q), mp);
	    return;
	}
    }

#if 0
    printf("qlsrvioc: %s message, command 0x%x\n",
	   (mp->b_datap->db_type == M_IOCDATA ? "IOCDATA" :
	    mp->b_datap->db_type == M_IOCTL ? "IOCTL" : "??"),
	    iocbp->ioc_cmd);
#endif

    switch (iocbp->ioc_cmd)
    {
    case TCSETAF:
	{
	    struct termio *cb;

	    flush(tp, FREAD);

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termio *)mp->b_cont->b_rptr;

	    tp->t_cflag = (tp->t_cflag & 0xffff0000 | cb->c_cflag);
	    tp->t_iflag = (tp->t_iflag & 0xffff0000 | cb->c_iflag);
	    tp->t_oflag = (tp->t_oflag & 0xffff0000 | cb->c_oflag);

	    qlparam(ql, OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCSETA:
    case TCSETAW:
	{
	    struct termio *cb;

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termio *)mp->b_cont->b_rptr;

	    tp->t_cflag = (tp->t_cflag & 0xffff0000 | cb->c_cflag);
	    tp->t_iflag = (tp->t_iflag & 0xffff0000 | cb->c_iflag);
	    tp->t_oflag = (tp->t_oflag & 0xffff0000 | cb->c_oflag);

	    qlparam(ql, OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCSETSF:
	{
	    struct termios *cb;

	    flush(tp, FREAD);

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termios *)mp->b_cont->b_rptr;

	    tp->t_cflag = cb->c_cflag;
	    tp->t_iflag = cb->c_iflag;
	    tp->t_oflag = cb->c_oflag;

	    qlparam(ql, OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCSETS:
    case TCSETSW:
	{
	    struct termios *cb;

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termios *)mp->b_cont->b_rptr;

	    tp->t_cflag = cb->c_cflag;
	    tp->t_iflag = cb->c_iflag;
	    tp->t_oflag = cb->c_oflag;

	    qlparam(ql, OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCGETA:
	{	/* immediate parm retrieve */
	    struct termio *cb;

	    if (mp->b_cont)		/* Bad user supplied parameter */
		freemsg(mp->b_cont);

	    if (!(bp1 = allocb(sizeof (struct termio), BPRI_MED)))
	    {
		putbq(q, mp);
		bufcall(sizeof (struct termio), BPRI_MED, getoblk, (long)tp);
		return;
	    }

	    mp->b_cont = bp1;
	    cb = (struct termio *)mp->b_cont->b_rptr;

	    cb->c_iflag = (unsigned short)tp->t_iflag;
	    cb->c_oflag = (unsigned short)tp->t_oflag;
	    cb->c_cflag = (unsigned short)tp->t_cflag;

	    mp->b_cont->b_wptr += sizeof (struct termio);
	    mp->b_datap->db_type = M_IOCACK;
	    iocbp->ioc_count = sizeof (struct termio);
	    putnext(RD(q), mp);
	    break;
	}

    case TCGETS:
	{	/* immediate parm retrieve */
	    struct termios *cb;

	    if (mp->b_cont)		/* Bad user supplied parameter */
		freemsg(mp->b_cont);

	    if (!(bp1 = allocb(sizeof(struct termios), BPRI_MED)))
	    {
		putbq(q, mp);
		bufcall(sizeof(struct termios), BPRI_MED, getoblk, (long)tp);
		return;
	    }
	    mp->b_cont = bp1;
	    cb = (struct termios *)mp->b_cont->b_rptr;

	    cb->c_iflag = tp->t_iflag;
	    cb->c_oflag = tp->t_oflag;
	    cb->c_cflag = tp->t_cflag;

	    mp->b_cont->b_wptr += sizeof(struct termios);
	    mp->b_datap->db_type = M_IOCACK;
	    iocbp->ioc_count = sizeof(struct termios);
	    putnext(RD(q), mp);
	    break;
	}

    case TCSBRK:
	mp->b_datap->db_type = (mp->b_cont ? M_IOCACK : M_IOCNAK);
	freemsg(unlinkb(mp));
	iocbp->ioc_count = 0;
	iocbp->ioc_rval = 0;
	iocbp->ioc_error = 0;
	putnext(RD(q), mp);
	qlproc(tp, T_BREAK);
	break;

    default:
	/* Unrecognized ioctl command */
	if (canput(RD(q)->q_next))
	{
	    mp->b_datap->db_type = M_IOCNAK;
	    putnext(RD(q), mp);
	}
	else
	    putbq(q, mp);
	break;
    }
}

static void ql_ttrstrt(ql_t *ql)
{
    qlparam(ql, OPEN);
    ql->tp->t_state &= ~TIMEOUT;
}

static void newcard(device_t *dp)
{
    device_t **pp;

    pp = (device_t **)endof(qladdr) - 1;

    if (pp[0])
    {
	printf("ql: too many cards\n");
	return;
    }

    while ((pp > qladdr) && (! pp[-1] || pp[-1] > dp))
    {
	pp[0] = pp[-1];
	--pp;
    }

    pp[0] = dp;
}

static void startcard(device_t *dp)
{
    unsigned char *ip, *op;

    ip = ql6502code;
    op = (unsigned char *)dp;

    while (ip < (unsigned char *)endof(ql6502code))
    {
	short s = *(short *)ip;

	ip += sizeof(s);

	if (s < 0)
	{
	    while (s++)
		*op++ = '\0';
	}
	else
	{
	    while (s--)
		*op++ = *ip++;
	}
    }

    dp->start = 0;
}

static void initialize(void)
{
    device_t *dp;
    unsigned int c, l;
    unsigned char *x;
    int s;
    static int done;

    if (done)
	return;

    done = TRUE;

    for (c = 0; autocon(0x02020046, c, &dp, &x); ++c)
	newcard(dp);

    for (c = 0; autocon(0x02020045, c, &dp, &x); ++c)
	newcard(dp);

    c = 0;

    while ((c < nel(qladdr)) && (dp = qladdr[c]))
    {
	startcard(dp);

	for (l = 0; l < NLINE; ++l)
	{
	    ql_board[c][l].tp = &ql_tty[c][l];
	    ql_board[c][l].lp = &dp->line[l];
	    ql_board[c][l].tbufp = dp->tbuf[l];
	    ql_board[c][l].rbufp = dp->rbuf[l];
	}
	++c;
    }
}

void qlintr(void)
{
    unsigned int c, l;

    for (c = 0; (c < NCARD) && (qladdr[c]); ++c)
    {
	qladdr[c]->irqack = 0;		/* ... just in case--rico */

	for (l = 0; l < NLINE; ++l)
	{
	    handle_exceptions(&ql_board[c][l]);
	    if (ql_board[c][l].tp->t_rdqp)
	    {
		input(&ql_board[c][l]);
		(void) getoblk(ql_board[c][l].tp);
	    }
	}
    }
}
@EOF

chmod 644 ql.c

echo x - sl.c
cat >sl.c <<'@EOF'
#include "sys/types.h"
#include "sys/param.h"
#include "sys/dir.h"
#include "sys/file.h"
#include "sys/signal.h"
#include "sys/termio.h"
#include "sys/termios.h"
#include "sys/stream.h"
#include "sys/stropts.h"
#include "sys/strtty.h"
#include "sys/errno.h"
#include "sys/conf.h"
#include "sys/sysmacros.h"
#include "sys/inline.h"
#include "sys/systm.h"
#include "sys/cred.h"
#include "amigahr.h"
#include "sl.h"


/* Amiga serdatr register: */
#define DATABITS 0x0FF
#define STOPBIT  0x100
#define DATASTOP 0x1FF

/* Amiga adkcon register: */
#define UARTBRK (1<<11)

void slinit(void);
static int slopen(struct queue *rq, dev_t *devp, int flag, int sflag,
		  cred_t *credp);
static int slclose(struct queue *q, int oflag, cred_t *credp);
static int getoblk(struct strtty *tp);
static void delay(struct strtty *tp);
static void flush(struct strtty *tp, int cmd);
static int slwput(struct queue *q, mblk_t *bp);
static void slproc(struct strtty *tp, unsigned cmd);
static void slputioc(queue_t *q, mblk_t *bp);
static int slwsrv();
static int slrsrv();
static void slparam(xmitter xm);
static void slsrvioc(queue_t *q, mblk_t *mp);
static void sl_ttrstrt(struct strtty *tp);
static void checkcarrier(void);
void slrint(void);
void slpoll(void);
void sltint(void);
static void output(unsigned int ch);

/*
 * Local stuff.
 */
sl_t sl = { INPUTGLOBSIZE, EVERYSOOFTEN, };
static int tictoc;

/*
 *  stream data structure definitions
 */

static struct module_info sl_minfo =
{
    0x736c, "sl", 0, INFPSZ, 256, 128,
};

static struct qinit slrinit =
{
    NULL, slrsrv, slopen, slclose, NULL, &sl_minfo, NULL,
};

static struct qinit slwinit =
{
    slwput, slwsrv, NULL, NULL, NULL, &sl_minfo, NULL,
};

struct streamtab slinfo =
{
    &slrinit, &slwinit, NULL, NULL,
};

/* One-time initialization */
static int initflag;

void slinit(void)
{
    ACIAB->ddra |= (DTR|RTS);	/* Outputs */
    ACIAB->ddra &= ~(CD|CTS);	/* Inputs */
    idisable(AIESRBF);
    idisable(AIESTBE);
    ++initflag;
}

static int slopen(struct queue *rq, dev_t *devp, int flag, int sflag,
		  cred_t *credp)
{
    int s, minor_device;

    if (sflag)			/* not a clone */
	return EINVAL;		/* or module */

    s = spltty();

    if (!initflag)
	slinit();

    {
	struct stroptions *sop;
	mblk_t *mop = allocb(sizeof (struct stroptions), BPRI_MED);

	if (!mop)
	{
	    splx(s);
	    return EAGAIN;
	}

	mop->b_datap->db_type = M_SETOPTS;
	sop = (struct stroptions *)mop->b_wptr;
	mop->b_wptr += sizeof (struct stroptions);
	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
	sop->so_hiwat = 512;
	sop->so_lowat = 256;

	putnext(rq, mop);
    }

    /*
     * Hardware flow control on if minor == 0, otherwise don't
     * do hardware flow control.
     */
    if (!(sl.tty.t_state & ISOPEN))
    {
	sl.output_mblk = NULL;
	sl.cts = TRUE;
	sl.tty.t_rdqp = rq;
	sl.tty.t_dev = getminor(*devp);	/* needed for hd flow ctl */
	sl.tty.t_line = 0;
	sl.tty.t_iflag = 0;
	sl.tty.t_oflag = 0;
	sl.tty.t_lflag = 0;
	sl.tty.t_cflag = B9600|SSPEED|CS8|CREAD|HUPCL;

	sl.carrier = FALSE;

	slparam(OPEN);
    }

    checkcarrier();

    if (flag & (FNDELAY|FNONBLOCK))
    {
	sl.tty.t_state |= CARR_ON;
	if (sl.tty.t_state & WOPEN)
	{
	    sl.tty.t_state &= ~WOPEN;
	    wakeup(&sl.tty);
	}
    }
    else
    {
	while (!(sl.tty.t_state & CARR_ON))
	{
	    sl.tty.t_state |= WOPEN;	/* Sleeping for carrier in open */
	    sleep(&sl.tty, TTIPRI);
	}
    }

    if (!sl.rdq)
    {
	sl.rdq = rq;
	sl.tty.t_state |= ISOPEN; /* In open */
	ienable(AIESRBF);
    }

    splx(s);

    return 0;
}

static int slclose(struct queue *q, int oflag, cred_t *credp)
{
    int s = spltty();
    struct strtty *tp = &sl.tty;

    if (tp->t_state & ISOPEN)
    {
	int cflag = tp->t_cflag;

	if (!(oflag & (FNDELAY|FNONBLOCK)))
	{
	    while (WR(q)->q_first || tp->t_state & BUSY)
	    {
		(void) getoblk(tp);

		if (WR(q)->q_first || tp->t_state & BUSY)
		{
		    tp->t_state |= TTIOW;
		    if (sleep((caddr_t)&tp->t_oflag, PZERO + 1| PCATCH))
		    {
			tp->t_state &= ~TTIOW;
			break;
		    }
		}
	    }
	}
    }

    if (sl.tty.t_cflag & HUPCL)
	slparam(CLOSE);

    tp->t_state = 0;
    tp->t_rdqp = NULL;
    sl.rdq = NULL;

    idisable(AIESRBF);
    idisable(AIESTBE);

    splx(s);

    return 0;
}

/*
 * Process an output block.
 * Returns nonzero if there was nothing in the queue.
 */

static int getoblk(struct strtty *tp)
{
    int s = spltty();
    struct queue *q;
    mblk_t *bp;

    if (!tp->t_rdqp)
    {
	splx(s);
	return 1;
    }
    q = WR(tp->t_rdqp);

    do
    {
	bp = getq(q);

	if (!bp)
	{
	    /* wakeup close write queue drain */
	    tp->t_state &= ~BUSY;

	    if (tp->t_state & TTIOW)
	    {
		tp->t_state &= ~TTIOW;
		wakeup(&tp->t_oflag);
	    }

	    splx(s);
	    return 1;
	}

	switch (bp->b_datap->db_type)
	{
	case M_DATA:
#if 0
	    printf("slgetoblk 0x%x: got DATA msg, %d bytes (%x %x %x)\n",
		   tp, blklen(bp), bp->b_rptr[0], bp->b_rptr[1],
		   bp->b_rptr[2]);
#endif
	    if (tp->t_state & (TTSTOP | TIMEOUT))
	    {
		putbq(q, bp);
		splx(s);
		return 0;
	    }

	    /*
	     * queue bytes to be sent out
	     */

	    if (sl.output_mblk)
		putbq(q, bp);
	    else
		sl.output_mblk = bp;

	    if (!(tp->t_state & BUSY))
	    {
		ienable(AIESTBE);

		if (sl.output_mblk->b_rptr >= sl.output_mblk->b_wptr)
		    freemsg(sl.output_mblk);
		else
		    output(*sl.output_mblk->b_rptr++);
	    }
	    break;

	case M_IOCTL:
	case M_IOCDATA:
	    slsrvioc(q, bp);
	    break;

	default:
	    printf("slgetoblk 0x%x: got an unknown message, type 0x%x\n",
		   tp, bp->b_datap->db_type);
	    freemsg(bp);
	    break;
	}
    } while (!(tp->t_state & BUSY));

    splx(s);
    return 0;
}

static void delay(struct strtty *tp)
{
    int s = spltty();

    tp->t_state &= ~TIMEOUT;

    splx(s);

    getoblk(tp);
}

static void flush(struct strtty *tp, int cmd)
{
    int s = spltty();

    if (cmd&FWRITE)
    {
	flushq(WR(tp->t_rdqp), FLUSHDATA);
	tp->t_state &= ~BUSY;

	if (tp->t_state & TTIOW)
	{
	    tp->t_state &= ~TTIOW;
	    wakeup(&tp->t_oflag);
	}

	slproc(tp, T_WFLUSH);
    }

    if (cmd&FREAD)
    {
	flushq(tp->t_rdqp, FLUSHDATA);

	slproc(tp, T_RFLUSH);
    }

    splx(s);
    getoblk(tp);
}

static int slwput(struct queue *q, mblk_t *bp)
{
    int s;
    struct strtty *tp = &sl.tty;
    mblk_t *bp1;

    switch (bp->b_datap->db_type)
    {
    case M_DATA:
	if (!(tp->t_state & CARR_ON))
	{
	    putq(q, bp);
	    return 0;
	}

	s = spltty();

	while (bp)
	{
	    bp->b_datap->db_type = M_DATA;
	    bp1 = unlinkb(bp);

	    if (blklen(bp) <= 0)
		freeb(bp);
	    else
		putq(q, bp);

	    bp = bp1;
	}

	splx(s);

	if (q->q_first)
	    getoblk(tp);
	break;

    case M_IOCTL:
    case M_IOCDATA:
	slputioc(q, bp);
	if (q->q_first)
	    getoblk(tp);
	break;

    case M_CTL:
	{
	    struct iocblk *iocb = (struct iocblk *)bp->b_rptr;
	    if ((blklen(bp) == sizeof *iocb) &&
		(iocb->ioc_cmd == MC_CANONQUERY))
		iocb->ioc_cmd = MC_DO_CANON;
	    else
		bp->b_datap->db_type = M_IOCNAK;
	    putnext(RD(q), bp);
	}
	break;

    case M_FLUSH:
	s = spltty();
	switch (bp->b_rptr[0])
	{
	case FLUSHRW:
	    flush(tp, (FREAD|FWRITE));
	    bp->b_rptr[0] = FLUSHR;
	    putnext(RD(q), bp);
	    break;

	case FLUSHR:
	    flush(tp, FREAD);
	    putnext(RD(q), bp);
	    break;

	case FLUSHW:
	    flush(tp, FWRITE);
	    freemsg(bp);
	    break;
	}
	splx(s);
	break;

    case M_BREAK:
	s = spltty();
	slproc(tp, T_BREAK);
	splx(s);
	freemsg(bp);
	break;

    case M_START:
	s = spltty();
	slproc(tp, T_RESUME);
	splx(s);
	freemsg(bp);
	getoblk(tp);
	break;

    case M_STOP:
	s = spltty();
	slproc(tp, T_SUSPEND);
	splx(s);
	freemsg(bp);
	break;

    case M_DELAY:
	s = spltty();
	tp->t_state |= TIMEOUT;
	timeout(delay, (caddr_t)tp, bp->b_rptr[0]);
	splx(s);
	freemsg(bp);
	break;

    case M_STARTI:
	/* Can't start/stop keyboard */
	freemsg(bp);
	break;

    case M_STOPI:
	/* Can't start/stop keyboard */
	freemsg(bp);
	break;

    case M_READ:
	/* <EH> Read queue is always up-to-date? */
	freemsg(bp);
	break;

    default:
	printf("slwput: got an unknown message 0x%x\n", bp->b_datap->db_type);
	freemsg(bp);
	break;
    }
}

/*
 * This does all the work.
 */

static void slproc(struct strtty *tp, unsigned cmd)
{
    int s;

    switch (cmd)
    {
    case T_TIME:
	s = spltty();
	tp->t_state &= ~TIMEOUT;
	splx(s);
	irequest(AIESTBE);
	getoblk(tp);
	break;

    case T_WFLUSH:
	if (sl.output_mblk)
	{
	    freemsg(sl.output_mblk);
	    sl.output_mblk = NULL;
	}
	/* fall through */
    case T_RESUME:			/* resume output */
	s = spltty();
	tp->t_state &= ~TTSTOP;
	splx(s);
	irequest(AIESTBE);
	getoblk(tp);
	break;

    case T_SUSPEND:			/* suspend output */
	s = spltty();
	tp->t_state |= TTSTOP;
	splx(s);
	break;

    case T_RFLUSH:
	break;

    case T_BREAK:
	s = spltty();
	slparam(SETBREAK);
	tp->t_state |= TIMEOUT;
	timeout(sl_ttrstrt, tp, HZ/4);
	splx(s);
	getoblk(tp);
	break;
    }
}

static void slputioc(queue_t *q, mblk_t *bp)
{
    struct iocblk *iocbp = (struct iocblk *)bp->b_rptr;
    struct strtty *tp = &sl.tty;
    mblk_t *bp1;

    switch (iocbp->ioc_cmd)
    {
    case TCSETA:
    case TCSETS:
	if (tp->t_state & BUSY)
	    putbq(q, bp);		/* queue these for later */
	else
	    slsrvioc(q, bp);
	return;

    case TCGETA:
    case TCGETS:			/* immediate parm retrieve */
	slsrvioc(q, bp);
	return;

    default:
	if ((iocbp->ioc_cmd&IOCTYPE) == LDIOC)
	{
	    bp->b_datap->db_type = M_IOCACK; /* ignore LDIOC cmds */
	    freemsg(unlinkb(bp));
	    iocbp->ioc_count = 0;
	    putnext(RD(q), bp);
	    return;
	}
	break;
    }

    /* Other IOCTLs get queued... */
    if (q->q_first || (tp->t_state & BUSY))
	putbq(q, bp);
    else
	slsrvioc(q, bp);
}

static int slwsrv() { return 0; }

static int slrsrv() { return 0; }

static void slparam(xmitter xm)
{
    unsigned int s;
    static unsigned short speeds[] =
    {
	0,			/* hangup */
	31 - 1,			/* 115200 for ql (hardware can't do 50) */
	62 - 1,			/* 57600 is popular (hardware can't do 75) */
	32542 - 1,		/* 110 */
	26714 - 1,		/* 134 */
	23864 - 1,		/* 150 */
	17898 - 1,		/* 200 */
	11932 - 1,		/* 300 */
	5966 - 1,		/* 600 */
	2983 - 1,		/* 1200 */
	1989 - 1,		/* 1800 */
	1492 - 1,		/* 2400 */
	746 - 1,		/* 4800 */
	373 - 1,		/* 9600 */
	186 - 1,		/* 19200 */
	93 - 1			/* 38400 */
    };

    switch ((int)xm)
    {
    case OPEN:
	s = sl.tty.t_cflag & CBAUD;

	if (s>0 && s < (sizeof(speeds) / sizeof(speeds[0])))
	{
	    AMIGA->serper = DATA8 | speeds[s];
	    ACIAB->pra &= ~DTR;
	}
	else
	    ACIAB->pra |= DTR;

	AMIGA->adkcon = ADKCLR | UARTBRK;

	if (!(sl.tty.t_iflag & IXON) && (sl.tty.t_state & TTSTOP))
	    slproc(&sl.tty, T_RESUME);
	break;

    case CLOSE:
	ACIAB->pra |= DTR;
	break;

    case SETBREAK:
	AMIGA->adkcon = ADKSET | UARTBRK;
    }
}

static void slsrvioc(queue_t *q, mblk_t *mp)
{
    struct strtty *tp = &sl.tty;
    struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
    int error;
    mblk_t *bp1;

    if (mp->b_datap->db_type == M_IOCDATA)
    {
	/* For copyin/copyout failures, just free message. */
	if (((struct copyresp *)mp->b_rptr)->cp_rval)
	{
	    freemsg(mp);
	    return;
	}

	if (!((struct copyresp *)mp->b_rptr)->cp_private)
	{
	    mp->b_datap->db_type = M_IOCACK;
	    freemsg(unlinkb(mp));
	    iocbp->ioc_count = 0;
	    iocbp->ioc_rval = 0;
	    iocbp->ioc_error = 0;
	    putnext(RD(q), mp);
	    return;
	}
    }

#if 0
    printf("slsrvioc: %s message, command 0x%x %d\n",
	   (mp->b_datap->db_type == M_IOCDATA ? "IOCDATA" :
	    mp->b_datap->db_type == M_IOCTL ? "IOCTL" : "??"),
	    iocbp->ioc_cmd, (sl.tty.t_state & CARR_ON));
#endif

    switch (iocbp->ioc_cmd)
    {
    case TCSETAF:
	{
	    struct termio *cb;

	    flush(tp, FREAD);

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termio *)mp->b_cont->b_rptr;

	    tp->t_cflag = (tp->t_cflag & 0xffff0000 | cb->c_cflag);
	    tp->t_iflag = (tp->t_iflag & 0xffff0000 | cb->c_iflag);
	    tp->t_oflag = (tp->t_oflag & 0xffff0000 | cb->c_oflag);

	    slparam(OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCSETA:
    case TCSETAW:
	{
	    struct termio *cb;

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termio *)mp->b_cont->b_rptr;

	    tp->t_cflag = (tp->t_cflag & 0xffff0000 | cb->c_cflag);
	    tp->t_iflag = (tp->t_iflag & 0xffff0000 | cb->c_iflag);
	    tp->t_oflag = (tp->t_oflag & 0xffff0000 | cb->c_oflag);

	    slparam(OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCSETSF:
	{
	    struct termios *cb;

	    flush(tp, FREAD);

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termios *)mp->b_cont->b_rptr;

	    tp->t_cflag = cb->c_cflag;
	    tp->t_iflag = cb->c_iflag;
	    tp->t_oflag = cb->c_oflag;

	    slparam(OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCSETS:
    case TCSETSW:
	{
	    struct termios *cb;

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }
	    cb = (struct termios *)mp->b_cont->b_rptr;

	    tp->t_cflag = cb->c_cflag;
	    tp->t_iflag = cb->c_iflag;
	    tp->t_oflag = cb->c_oflag;

	    slparam(OPEN);

	    mp->b_datap->db_type = M_IOCACK;
	    bp1 = unlinkb(mp);
	    if (bp1)
		freeb(bp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    case TCGETA:
	{	/* immediate parm retrieve */
	    struct termio *cb;

	    if (mp->b_cont)		/* Bad user supplied parameter */
		freemsg(mp->b_cont);

	    if (!(bp1 = allocb(sizeof (struct termio), BPRI_MED)))
	    {
		putbq(q, mp);
		bufcall(sizeof (struct termio), BPRI_MED, getoblk, (long)tp);
		return;
	    }

	    mp->b_cont = bp1;
	    cb = (struct termio *)mp->b_cont->b_rptr;

	    cb->c_iflag = (unsigned short)tp->t_iflag;
	    cb->c_oflag = (unsigned short)tp->t_oflag;
	    cb->c_cflag = (unsigned short)tp->t_cflag;

	    mp->b_cont->b_wptr += sizeof (struct termio);
	    mp->b_datap->db_type = M_IOCACK;
	    iocbp->ioc_count = sizeof (struct termio);
	    putnext(RD(q), mp);
	    break;
	}

    case TCGETS:
	{	/* immediate parm retrieve */
	    struct termios *cb;

	    if (mp->b_cont)		/* Bad user supplied parameter */
		freemsg(mp->b_cont);

	    if (!(bp1 = allocb(sizeof(struct termios), BPRI_MED)))
	    {
		putbq(q, mp);
		bufcall(sizeof(struct termios), BPRI_MED, getoblk, (long)tp);
		return;
	    }
	    mp->b_cont = bp1;
	    cb = (struct termios *)mp->b_cont->b_rptr;

	    cb->c_iflag = tp->t_iflag;
	    cb->c_oflag = tp->t_oflag;
	    cb->c_cflag = tp->t_cflag;

	    mp->b_cont->b_wptr += sizeof(struct termios);
	    mp->b_datap->db_type = M_IOCACK;
	    iocbp->ioc_count = sizeof(struct termios);
	    putnext(RD(q), mp);
	    break;
	}

    case TCSBRK:
	mp->b_datap->db_type = (mp->b_cont ? M_IOCACK : M_IOCNAK);
	freemsg(unlinkb(mp));
	iocbp->ioc_count = 0;
	iocbp->ioc_rval = 0;
	iocbp->ioc_error = 0;
	putnext(RD(q), mp);
	slproc(tp, T_BREAK);
	break;

    default:
	/* Unrecognized ioctl command */
	if (canput(RD(q)->q_next))
	{
	    mp->b_datap->db_type = M_IOCNAK;
	    putnext(RD(q), mp);
	}
	else
	    putbq(q, mp);
	break;
    }
}

static void sl_ttrstrt(struct strtty *tp)
{
    slparam(OPEN);
    slproc(tp, T_TIME);
}

static void checkcarrier(void)
{
    int	s;

    s = spltty();

    if ((ACIAB->pra&CD) == 0)
    {
	if (!sl.carrier)
	{
	    sl.carrier = TRUE;
	    sl.tty.t_state |= CARR_ON;

	    if (sl.tty.t_state & WOPEN)
	    {
		sl.tty.t_state &= ~WOPEN;
		wakeup(&sl.tty);
	    }
	}
    }
    else
    {
	if (sl.carrier)
	{
	    sl.carrier = FALSE;

	    if (tictoc)
		untimeout(tictoc);

	    if (!(sl.tty.t_state & ISOPEN))
		sl.tty.t_state &= ~CARR_ON;
	    else if (!(sl.tty.t_cflag & CLOCAL))
	    {
		flush(&sl.tty, FREAD|FWRITE);
		sl.tty.t_state &= ~CARR_ON;

		if (sl.rdq)
		    putctl1(sl.rdq->q_next, M_HANGUP, 0);
	    }
	}
    }

    splx(s);
}

#define BUFsize   256U
#define HIGHwater 64
#define LOWwater  32
static volatile	unsigned int head, tail;
static unsigned short buff[BUFsize];

/* slrint runs from level 5 hardware interrupt */
void slrint(void)
{
    unsigned int nbytes = (BUFsize+head-tail)%BUFsize;
    unsigned short c = AMIGA->serdatr & DATASTOP;

    if (sl.tty.t_iflag & IXON)
    {
	if (c == (CSTART|STOPBIT))
	{
	    sl.output_xoff = 0;
	    return;
	}
	if (c == (CSTOP|STOPBIT))
	{
	    sl.output_xoff = 1;
	    return;
	}

	if (sl.tty.t_iflag & IXANY)
	    sl.output_xoff = 0;
    }

    if (nbytes >= BUFsize-1)
    {
	idisable(AIESRBF);
	return;
    }

    if (nbytes > BUFsize-LOWwater)
	ACIAB->pra |= RTS;	/* RTS off */

    buff[head] = c;
    head = (head+1) % BUFsize;
}

/* slpoll runs at clock priority */
void slpoll(void)
{
    mblk_t *mp;

    if (hardware_flow_control && sl.tty.t_state&ISOPEN)
    {
	if (!sl.output_xoff && (sl.tty.t_state & TTSTOP))
	    slproc(&sl.tty, T_RESUME);

	if (!sl.cts && !(ACIAB->pra&CTS))
	{
	    sl.cts = TRUE;
	    irequest(AIESTBE);	/* Try to send another byte */
	}
    }
    if (sl.tty.t_state & (WOPEN|ISOPEN))
	checkcarrier();

    mp = 0;
    while (sl.rdq && tail != head && canput(sl.rdq->q_next))
    {
	unsigned short c = buff[tail];

	if (!(c & DATASTOP))	/* No bits indicates a break */
	{
	    tail = (tail+1) % BUFsize;

	    if (sl.tty.t_iflag & IGNBRK)
		continue;

	    if (sl.tty.t_lflag & NOFLSH)
	    {
		if (mp)
		    putnext(sl.rdq, mp);
	    }
	    else
	    {
		flush(&sl.tty, (FREAD|FWRITE));
		freemsg(mp);
	    }
	    mp = 0;

	    putctl1(sl.rdq->q_next, M_BREAK, 0);
	    if (sl.tty.t_iflag & BRKINT)
		putctl1(sl.rdq->q_next, M_SIG, SIGINT);
	}
	else
	{
	    if (mp && mp->b_wptr >= mp->b_datap->db_lim)
	    {
		putnext(sl.rdq, mp);
		mp = 0;
	    }

	    if (!mp)
		mp = allocb(sl.inputglobsize, BPRI_HI);

	    if (!mp)
		break;

	    tail = (tail+1) % BUFsize;

	    c &= DATABITS;	/* Get the real character */

	    if ((sl.tty.t_iflag & ISTRIP) || (sl.tty.t_cflag & PARENB))
		c &= 0x7F;	/* Strip if wanted */

	    *mp->b_wptr++ = c;
	}

	ienable(AIESRBF);
	if ((BUFsize+head-tail)%BUFsize < BUFsize-HIGHwater)
	    ACIAB->pra &= ~RTS;	/* RTS on */
    }

    if (mp)
	putnext(sl.rdq, mp);
}

void sltint(void)
{
    unsigned int ch = (unsigned int)-1;

    if (!(AMIGA->serdatr&TBE))
	return;

    if (!sl.rdq)
	return;

    if (hardware_flow_control)
    {
	if (ACIAB->pra&CTS)		/* Not Clear To Send? */
	{
	    sl.cts = FALSE;
	    sl.tty.t_state |= BUSY;
	    return;
	}
	else
	{
	    sl.tty.t_state &= ~BUSY;
	    
	    if (sl.tty.t_state & TTIOW)
	    {
		sl.tty.t_state &= ~TTIOW;
		wakeup(&sl.tty.t_oflag);
	    }
	}
    }

    if (sl.output_xoff && !(sl.tty.t_state & TTSTOP))
	slproc(&sl.tty, T_SUSPEND);

    if (sl.tty.t_state & TTXON)
    {
	if (sl.tty.t_iflag & IXOFF)
	    ch = CSTART;

	sl.tty.t_state &= ~TTXON;
    }
    else if (sl.tty.t_state & TTXOFF)
    {
	if (sl.tty.t_iflag & IXOFF)
	    ch = CSTOP;

	sl.tty.t_state &= ~TTXOFF;
    }
    else if (sl.tty.t_state & TTSTOP)
    {
	idisable(AIESTBE);	/* No interrupts for a while */
	return;
    }

    if (ch == (unsigned int)-1)
	getoblk(&sl.tty);
    else
	output(ch);
}

static void output(unsigned int ch)
{
    if (sl.tty.t_cflag & PARENB)
    {
	extern unsigned char partab[];

	ch &= 0x7F;
	ch |= partab[ch] & 0x80;
	if (sl.tty.t_cflag & PARODD)
	    ch ^= 0x80;
    }

    AMIGA->serdat = STOPBIT | ch;	/* Or'd stop bit in */

    sl.tty.t_state |= BUSY;
}
@EOF

chmod 644 sl.c

exit 0