SysIII/usr/src/uts/vax/io/dzb.c
/*
* UTS driver for DZ-11 with KMC assist
*/
#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/ioctl.h"
#include "sys/uba.h"
#include "sys/sysinfo.h"
struct device *dzb_addr[];
int dzb_cnt;
struct tty dzb_tty[];
short dzb_scan;
short dzb_stat;
short dzb_kmc[8];
char dzb_speeds[16] = {
0, 00, 01, 02, 03, 04, 0, 05,
06, 07, 010, 012, 014, 016, 0, 0
};
/*
* Define op codes for commands to the KMC
*/
#define XBUFIN 0
#define IOCTL 1
#define BASEIN 2
#define RBUFIN 3
#define CMDIN 4
/*
* Define sub command bits for CMDIN
*/
#define IFLUSH (1<<0) /* Flush input */
#define OFLUSH (1<<1) /* Flush output */
#define OSPND (1<<2) /* Suspend output */
#define ORSME (1<<3) /* Resume output */
#define SCHAR (1<<4) /* Send character in csr6 */
#define SETTMR (1<<5) /* Set kmc ITIME to value in csr7 */
#define SBRK (1<<6) /* Send break */
/*
* Report codes for reports from the KMC
*/
#define XBUFOUT 0
#define RBUFOUT 1
#define RBUF1OT 2
#define RBRK 3
#define COCHG 4
#define ERROUT 5
#define BITS6 010
#define BITS7 020
#define TWOSB 040
#define PENABLE 0100
#define OPAR 0200
#define RCVENB 010000
#define IE 040
struct device {
short dzbcsr, dzbrbuf;
char dzbtcr, dzbdtr;
char dzbtbuf, dzbbrk;
};
#define dzblpr dzbrbuf
#define dzbmsr dzbbrk
#define ON 1
#define OFF 0
dzbopen(dev, flag)
{
register struct tty *tp;
register kf;
extern dzbproc(), dzbkint();
if ((dev&077) >= dzb_cnt) {
u.u_error = ENXIO;
return;
}
kf = 1<<(((dev>>6)&03)+8);
if ((dzb_stat&kf) == 0) {
if (kmcset(dev,03,dzbkint)) {
u.u_error = ENXIO;
return;
}
dzb_stat |= kf;
}
tp = &dzb_tty[dev&077];
if ((tp->t_state&(ISOPEN|WOPEN)) == 0) {
ttinit(tp);
tp->t_proc = dzbproc;
tp->t_state |= EXTPROC;
dzbparam(dev);
}
spl5();
if (tp->t_cflag&CLOCAL || dzbmodem(dev, ON))
tp->t_state |= CARR_ON;
else
tp->t_state &= ~CARR_ON;
if (!(flag&FNDELAY))
while ((tp->t_state&CARR_ON)==0) {
tp->t_state |= WOPEN;
sleep((caddr_t)&tp->t_canq, TTIPRI);
}
(*linesw[tp->t_line].l_open)(tp);
spl0();
}
dzbclose(dev)
{
register struct tty *tp;
tp = &dzb_tty[dev&077];
(*linesw[tp->t_line].l_close)(tp);
if (tp->t_cflag&HUPCL)
dzbmodem(dev, OFF);
}
dzbread(dev)
{
register struct tty *tp;
tp = &dzb_tty[dev&077];
(*linesw[tp->t_line].l_read)(tp);
}
dzbwrite(dev)
{
register struct tty *tp;
tp = &dzb_tty[dev&077];
(*linesw[tp->t_line].l_write)(tp);
}
dzbioctl(dev, cmd, arg, mode)
register dev;
{
register sel4,sel6;
switch(cmd) {
case TCDSET:
sel4 = SETTMR;
sel6 = arg<<8;
kmcload(dzb_kmc[(dev>>3)&07]|(dev&07), CMDIN, sel4, sel6);
break;
default:
if (ttiocom(&dzb_tty[dev&077], cmd, arg, mode))
dzbparam(dev);
}
}
dzbparam(dev)
{
register struct tty *tp;
register struct device *dzbaddr;
register flags, lpr;
register struct cblock *cp;
register i;
tp = &dzb_tty[dev&077];
dzbaddr= dzb_addr[(dev>>3)&07];
dzbaddr->dzbcsr = IE;
if (dzb_scan==0) {
dzbscan();
dzb_scan++;
}
flags = tp->t_cflag;
if ((flags&CBAUD) == 0) {
/* hang up line */
dzbmodem(dev, OFF);
return;
}
lpr = (dzb_speeds[flags&CBAUD]<<8)|(dev&07);
if (flags&CREAD)
lpr |= RCVENB;
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;
dzbaddr->dzblpr = lpr;
/*
* Now pass the relevant parameters to the KMC
*/
lpr = (dev>>3)&07;
if ((dzb_stat&(1<<lpr)) == 0) {
dzb_kmc[lpr] = dev&0370;
kmcload(dzb_kmc[lpr]|(dev&07),BASEIN,(int)dzb_addr[lpr]-UBA_DEV,0);
for (i=0; i<2; i++) {
paddr_t addr;
if ((cp = getcf()) == NULL)
break;
addr = ubmdata(cp);
if (kmcload(dzb_kmc[(dev>>3)&07]|(dev&07),
RBUFIN, loword(addr), hiword(addr)) < 0)
putcf(cp);
}
dzb_stat |= 1<<lpr;
}
kmcload(dzb_kmc[lpr]|(dev&07), IOCTL, tp->t_iflag, tp->t_oflag);
}
/*
* Routine to handle interrupts from the KMC
* This routine gets called from the KMC driver whenever
* the KMC generates an unsolicited interrupt (VEC4 == 1)
*
* These interrupts are used by the KMC to notify dzb.c
* of events such as output buffer completions
*/
dzbkint(dev, sel2, sel4, sel6)
{
register struct tty *tp;
register struct cblock *cp;
paddr_t addr;
tp = &dzb_tty[dev&077];
switch(sel2&7) {
case RBUFOUT:
sysinfo.rcvint++;
cp = (struct cblock *)ubmrev(sel4, sel6);
(*linesw[tp->t_line].l_input)(tp, cp, cp->c_last-cp->c_first);
if ((cp = getcf()) == NULL)
break;
addr = ubmdata(cp);
if (kmcload(dzb_kmc[(dev>>3)&07]|(dev&07),
RBUFIN, loword(addr), hiword(addr)) < 0)
putcf(cp);
break;
case RBUF1OT:
sysinfo.rcvint++;
(*linesw[tp->t_line].l_input)(tp, sel4&0377, 1);
if (((sel6>>8)&0377) > 1) {
(*linesw[tp->t_line].l_input)(tp, ((sel4>>8)&0377), 1);
if (((sel6>>8)&0377) > 2)
(*linesw[tp->t_line].l_input)(tp, sel6, 1);
}
break;
case XBUFOUT:
sysinfo.xmtint++;
cp = (struct cblock *)ubmrev(sel4, sel6);
if ((tp->t_buf == NULL) || (tp->t_buf != cp)) {
printf("dzb: xbufout\n");
break;
}
tp->t_state &= ~BUSY;
dzbproc(tp, T_OUTPUT);
break;
case RBRK:
sysinfo.rcvint++;
signal(tp->t_pgrp, SIGINT);
ttyflush(tp, (FREAD|FWRITE));
break;
default:
printf("dzb: %o %o %o %o\n", dev, sel2, sel4, sel6);
}
}
dzbproc(tp, cmd)
register struct tty *tp;
{
extern ttrstrt();
register dev;
register sel4, sel6;
dev = tp - dzb_tty;
sel4 = 0;
switch(cmd) {
case T_WFLUSH:
sel4 |= OFLUSH;
case T_RESUME:
sel4 |= ORSME;
goto start;
case T_OUTPUT:
start:
if (tp->t_state&(TIMEOUT|TTSTOP|BUSY))
break;
if (tp->t_state&TTIOW && tp->t_outq.c_cc==0) {
tp->t_state &= ~TTIOW;
wakeup((caddr_t)&tp->t_oflag);
}
if (tp->t_buf!=NULL)
putcf(tp->t_buf);
if ((tp->t_buf = getcb(&tp->t_outq)) != NULL) {
paddr_t addr;
addr = ubmdata(tp->t_buf);
if (kmcload(dzb_kmc[(dev>>3)&07]|(dev&07),
XBUFIN, loword(addr), hiword(addr)) < 0)
goto start;
tp->t_state |= BUSY;
}
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:
sel4 |= OSPND;
break;
case T_BLOCK:
tp->t_state |= TBLOCK;
sel4 |= SCHAR;
sel6 = CSTOP;
break;
case T_RFLUSH:
sel4 |= IFLUSH;
if (!(tp->t_state&TBLOCK))
break;
case T_UNBLOCK:
tp->t_state &= ~TBLOCK;
sel4 |= SCHAR;
sel6 = CSTART;
break;
case T_BREAK:
sel4 |= SBRK;
break;
}
if (sel4)
kmcload(dzb_kmc[(dev>>3)&07]|(dev&07), CMDIN, sel4, sel6);
}
dzbmodem(dev, flag)
{
register struct device *dzbaddr;
register bit;
dzbaddr = dzb_addr[(dev>>3)&07];
bit = 1<<(dev&07);
if (flag==OFF)
dzbaddr->dzbdtr &= ~bit;
else
dzbaddr->dzbdtr |= bit;
return(dzbaddr->dzbmsr & bit);
}
dzbscan()
{
register i;
register struct device *dzbaddr;
register struct tty *tp;
char bit;
for (i=0; i<dzb_cnt; i++) {
tp = &dzb_tty[i];
if (!(tp->t_state&(ISOPEN|WOPEN)))
continue;
dzbaddr = dzb_addr[i>>3];
bit = 1<<(i&07);
if (tp->t_cflag&CLOCAL || dzbaddr->dzbmsr&bit) {
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);
dzbaddr->dzbdtr &= ~bit;
ttyflush(tp, (FREAD|FWRITE));
}
tp->t_state &= ~CARR_ON;
}
}
}
timeout(dzbscan, 0, 120);
}
dzbclr()
{
register dev;
register struct tty *tp;
if (dzb_stat == 0)
return;
for (dev = 0; dev < 4; dev++)
if (dzb_stat&(1<<(dev+8)))
kmcdclr(dev);
dzb_stat = 0;
for (dev = 0; dev < dzb_cnt; dev++) {
tp = &dzb_tty[dev];
if ((tp->t_state&(ISOPEN|WOPEN)) == 0)
continue;
dzbparam(dzb_kmc[(dev>>3)&07]|(dev&07));
dzbmodem(dev, ON);
tp->t_state &= ~BUSY;
dzbproc(tp, T_OUTPUT);
}
}