SysIII/usr/src/uts/pdp11/io/dh.c
/*
* DH11 & DM11-BB driver for multiple units
*/
#include "sys/param.h"
#include "sys/dir.h"
#include "sys/user.h"
#include "sys/file.h"
#include "sys/tty.h"
#include "sys/conf.h"
#include "sys/sysinfo.h"
struct device *dh_addr[];
int dh_cnt;
struct tty dh_tty[];
struct inter dm_inter[];
short dhsar[8];
static char dh_xon = CSTART;
static char dh_xoff = CSTOP;
#define BITS6 01
#define BITS7 02
#define TWOSB 04
#define PENABLE 020
#define OPAR 040
#define IENAB 030100
#define XINT 0100000
#define CLRNEM 0400
struct device {
short dhcsr, dhnxch, dhlpr, dhcar;
short dhbcr, dhbar, dhbrk, dhsilo;
};
struct devdm *dm_addr[];
#define DONE 0200
#define SCENABL 040
#define BSY 020
#define TURNON 07 /* RTS, DTR, line enable */
#define TURNOFF 1 /* line enable only */
#define CARRIER 0100
struct devdm {
short dmcsr, dmlstat;
};
dhopen(dev, flag)
{
register struct tty *tp;
extern dhproc();
if (dev >= dh_cnt) {
u.u_error = ENXIO;
return;
}
tp = &dh_tty[dev];
if ((tp->t_state&(ISOPEN|WOPEN)) == 0) {
ttinit(tp);
tp->t_proc = dhproc;
dhparam(dev);
}
spl5();
if (tp->t_cflag&CLOCAL || dmctl(dev, TURNON)&CARRIER)
tp->t_state |= CARR_ON;
if (!(flag&FNDELAY))
while ((tp->t_state&CARR_ON)==0) {
tp->t_state |= WOPEN;
sleep(&tp->t_canq, TTIPRI);
}
(*linesw[tp->t_line].l_open)(tp);
spl0();
}
dhclose(dev)
{
register struct tty *tp;
tp = &dh_tty[dev];
(*linesw[tp->t_line].l_close)(tp);
if (tp->t_cflag&HUPCL) {
spl5();
dmctl(dev, TURNOFF);
spl0();
}
}
dhread(dev)
{
register struct tty *tp;
tp = &dh_tty[dev];
(*linesw[tp->t_line].l_read)(tp);
}
dhwrite(dev)
{
register struct tty *tp;
tp = &dh_tty[dev];
(*linesw[tp->t_line].l_write)(tp);
}
dhioctl(dev, cmd, arg, mode)
register dev;
{
switch(cmd) {
default:
if (ttiocom(&dh_tty[dev], cmd, arg, mode))
dhparam(dev);
}
}
dhparam(dev)
{
register struct tty *tp;
register flags, lpr;
register struct device *rp;
tp = &dh_tty[dev];
rp = dh_addr[dev>>4];
flags = tp->t_cflag;
lpr = (flags&CBAUD)<<10;
if (flags&CREAD)
lpr |= (flags&CBAUD)<<6;
if (flags&CS6)
lpr |= BITS6;
if (flags&CS7)
lpr |= BITS7;
if (flags&PARENB) {
lpr |= PENABLE;
if (flags&PARODD)
lpr |= OPAR;
}
if (flags&CSTOPB)
lpr |= TWOSB;
spl5();
rp->dhcsr &= ~017;
rp->dhcsr |= (dev&017) | IENAB;
if ((flags&CBAUD) == 0) {
/* Hang up line */
if (tp->t_state&CARR_ON) {
rp->dhbcr = -1;
dmctl(dev, TURNOFF);
}
spl0();
return;
}
rp->dhlpr = lpr;
spl0();
}
dhrint(dev)
{
register struct tty *tp;
register c;
register struct device *rp;
sysinfo.rcvint++;
rp = dh_addr[dev];
while ((c = rp->dhnxch) < 0) { /* char. present */
tp = &dh_tty[((c>>8)&017)|(dev<<4)];
if (tp >= &dh_tty[dh_cnt])
continue;
if (!(tp->t_state&(ISOPEN|WOPEN)))
continue;
(*linesw[tp->t_line].l_input)(tp, c, 0);
}
}
/*
* DH11 transmitter interrupt.
* Restart each line which used to be active but has
* terminated transmission since the last interrupt.
*/
dhxint(dev)
{
register struct tty *tp;
register ttybit, bar;
struct device *rp;
short *sbar;
sysinfo.xmtint++;
rp = dh_addr[dev];
rp->dhcsr &= ~XINT;
rp->dhcsr |= CLRNEM;
sbar = &dhsar[dev];
bar = *sbar & ~rp->dhbar;
dev <<= 4;
ttybit = 1;
for (; bar; dev++, ttybit <<= 1) if (bar&ttybit) {
tp = &dh_tty[dev];
bar &= ~ttybit;
lobyte(rp->dhcsr) = (dev&017) | IENAB;
if (tp->t_state&BUSY) {
register n;
tp->t_state &= ~BUSY;
n = rp->dhcar - &tp->t_buf->c_data[tp->t_buf->c_first];
tp->t_buf->c_first += n;
}
if (tp->t_state&TTXON) {
tp->t_state &= ~TTXON;
rp->dhcar = &dh_xon;
rp->dhbcr = -1;
rp->dhbar |= ttybit;
continue;
}
if (tp->t_state&TTXOFF) {
tp->t_state &= ~TTXOFF;
rp->dhcar = &dh_xoff;
rp->dhbcr = -1;
rp->dhbar |= ttybit;
continue;
}
*sbar &= ~ttybit;
dhproc(tp, T_OUTPUT);
}
}
dhproc(tp, cmd)
register struct tty *tp;
{
register struct device *rp;
register n;
char *cp;
int dev, ttybit;
extern ttrstrt();
dev = tp - dh_tty;
rp = dh_addr[dev>>4];
ttybit = 1 << (dev&017);
switch(cmd) {
case T_TIME:
tp->t_state &= ~TIMEOUT;
rp->dhbrk &= ~ttybit;
goto start;
case T_WFLUSH:
case T_RESUME:
tp->t_state &= ~TTSTOP;
goto start;
case T_OUTPUT:
start:
if (tp->t_state&(TIMEOUT|TTSTOP|BUSY))
break;
if (tp->t_buf && tp->t_buf->c_first >= tp->t_buf->c_last) {
putcf(tp->t_buf);
tp->t_buf = NULL;
}
if (tp->t_buf == NULL) {
if ((tp->t_buf = getcb(&tp->t_outq)) == NULL) {
if (tp->t_state&TTIOW) {
tp->t_state &= ~TTIOW;
wakeup((caddr_t)&tp->t_oflag);
}
goto dd;
}
}
n = tp->t_buf->c_last - tp->t_buf->c_first;
cp = &tp->t_buf->c_data[tp->t_buf->c_first];
if (tp->t_oflag&OPOST) {
register char *p;
p = cp;
do {
if (((*p++)&0377) == 0200) {
p--;
break;
}
} while(--n);
n = p - cp;
}
if (n == 0) {
if ((tp->t_buf->c_first+1) == tp->t_buf->c_last) {
n = getc(&tp->t_outq);
*cp = n;
} else {
tp->t_buf->c_first++;
cp++;
n = *cp & 0377;
}
if (n > 0200) {
tp->t_buf->c_first++;
timeout(ttrstrt, tp, (n&0177)+6);
tp->t_state |= TIMEOUT;
goto dd;
}
n = 1;
}
lobyte(rp->dhcsr) = (dev&017) | IENAB;
rp->dhcar = cp;
rp->dhbcr = -n;
rp->dhbar |= ttybit;
dhsar[dev>>4] |= ttybit;
tp->t_state |= BUSY;
dd:
if (tp->t_state&OASLP &&
tp->t_outq.c_cc <= ttlowat[tp->t_cflag&CBAUD]) {
tp->t_state &= ~OASLP;
wakeup((caddr_t)&tp->t_outq);
}
break;
case T_SUSPEND:
tp->t_state |= TTSTOP;
if (tp->t_state&BUSY) {
lobyte(rp->dhcsr) = (dev&017) | IENAB;
rp->dhbcr = -1;
}
break;
case T_BLOCK:
tp->t_state |= TBLOCK;
tp->t_state &= ~TTXON;
lobyte(rp->dhcsr) = (dev&017) | IENAB;
if (tp->t_state&BUSY) {
tp->t_state |= TTXOFF;
rp->dhbcr = -1;
} else {
rp->dhcar = &dh_xoff;
rp->dhbcr = -1;
rp->dhbar |= ttybit;
dhsar[dev>>4] |= ttybit;
}
break;
case T_RFLUSH:
if (!(tp->t_state&TBLOCK))
break;
case T_UNBLOCK:
tp->t_state &= ~(TTXOFF|TBLOCK);
lobyte(rp->dhcsr) = (dev&017) | IENAB;
if (tp->t_state&BUSY) {
tp->t_state |= TTXON;
rp->dhbcr = -1;
} else {
rp->dhcar = &dh_xon;
rp->dhbcr = -1;
rp->dhbar |= ttybit;
dhsar[dev>>4] |= ttybit;
}
break;
case T_BREAK:
rp->dhbrk |= ttybit;
tp->t_state |= TIMEOUT;
timeout(ttrstrt, tp, HZ/4);
break;
}
}
/*
* Dump control bits into the DM registers.
*/
dmctl(dev, bits)
register dev;
{
register struct devdm *dmaddr;
register svcsr;
sysinfo.mdmint++;
dmaddr = dm_addr[dev>>4];
dmaddr->dmcsr &= ~SCENABL;
while((svcsr=dmaddr->dmcsr)&BSY);
dmaddr->dmcsr = dev&017;
dmaddr->dmlstat = bits;
dev = dmaddr->dmlstat;
dmaddr->dmcsr = svcsr|IENAB|SCENABL;
return(dev);
}
/*
* DM11 interrupt.
* Mainly, deal with carrier transitions.
*/
dmintr(dev)
{
register struct tty *tp;
register struct devdm *dmaddr;
register unit;
dmaddr = dm_addr[dev];
unit = (dmaddr->dmcsr&017)|(dev<<4);
if (unit>=dh_cnt) {
dmaddr->dmlstat = 0;
dmaddr->dmcsr &= ~DONE;
return;
}
tp = &dh_tty[unit];
dm_inter[unit].cnt++;
if (tp->t_cflag&CLOCAL) {
dmaddr->dmlstat = 0;
dmaddr->dmcsr &= ~DONE;
return;
}
if (dmaddr->dmlstat&CARRIER) {
if ((tp->t_state&CARR_ON)==0) {
wakeup(&tp->t_canq);
tp->t_state |= CARR_ON;
}
} else {
if (tp->t_state&CARR_ON) {
if (tp->t_state&ISOPEN) {
signal(tp->t_pgrp, SIGHUP);
dmaddr->dmlstat = 0;
ttyflush(tp, FREAD|FWRITE);
}
tp->t_state &= ~CARR_ON;
}
}
dmaddr->dmcsr &= ~DONE;
}
dhclr()
{
register dev;
register struct tty *tp;
for (dev = 0; dev < dh_cnt; dev++) {
tp = &dh_tty[dev];
if ((tp->t_state&(ISOPEN|WOPEN)) == 0)
continue;
dhparam(dev);
dmctl(dev, TURNON);
tp->t_state &= ~BUSY;
dhproc(tp, T_OUTPUT);
}
}