SRI-NOSC/dmr/dh.c
#
/*
* DH-11 driver
* This driver calls on the DHDM driver.
* If the DH has no DM11-BB, then the latter will
* be fake. To insure loading of the correct DM code,
* lib2 should have dhdm.o, dh.o and dhfdm.o in that order.
*
* This driver was modified to allow the use of multiple
* DH11s. To avail yourself of this feature (or to disable
* it) you should do the following:
*
* In this module: Initialize the global array dhaddrs with
* the base addresses for your dh11s. Initialize NDH11
* to the total number of dh11 ports in your system.
* Dimension dhsar to the number of dh11s you have.
*
* In l.s: When you initialize the interrupt vectors for
* your dh11s, add to each interrupt PS a dh11 index (0
* for the first dh11, 1 for the second etc). This
* number will be used to index into dhaddrs to
* determine which set of registers caused the
* interrupt. It allows all sets of dh registers to use
* the same interrupt routines.
*
* globals:
*
* dhaddrs contains base addresses for the various dh11s in the
* system.
*
* dhsar contains (for each dh11) a copy of the bar as it
* was last known to be.
*
* history:
*
* Initially a Release six driver.
* Modified by Dennis L. Mumaugh for multiple DH/DM support
* 15 April 1977
* Thanks to Mark Kampe (UCLA) for assistance
*/
#include "param.h"
#include "conf.h"
#include "user.h"
/*#include "userx.h"*/
#include "tty.h"
#include "proc.h"
#ifdef DNTTY
#include "baudot.h" /* DN: file containing baudot mapping tables */
#define VRTMO 20+20 /* 60'ths of sec to wait for dropping RTS */
#define IDLTMO (15*60*60)&077777 /* 60'ths of sec to sleep between idle chks */
#endif DNTTY
#ifndef NDH11
#define NDH11 32 /* number of lines */
#endif NDH11
#define DHNCH 8 /* max number of DMA chars */
struct tty dh11[NDH11];
/*
* Place from which to do DMA on output
*/
char dh_clist[NDH11][DHNCH];
/*
* Used to communicate the number of lines to the DM
*/
int ndh11 NDH11;
/*
* Hardware control bits
*/
#ifdef DNTTY
#define BITS5 00 /* DN: for Baudot terms */
#endif DNTTY
#define BITS6 01
#define BITS7 02
#define BITS8 03
#define TWOSB 04
#define PENABLE 020
#define OPAR 040 /* Old DEC manuals wrongly say this sets EVEN parity.*/
#define HDUPLX 040000
#define IENABLE 030100
#define PERROR 010000
#define FRERROR 020000
#define XINT 0100000
#define SSPEED 7 /* standard speed: 300 baud */
/*
* DM control bits
*/
#define TURNON 03 /* CD lead + line enable */
#define TURNOFF 01 /* line enable */
#define RQS 04 /* request to send */
#define XCLUDE 0200 /* exclusive-use flag against open */
/*
* Software copy of last dhbar
*/
int dhsar[2];
struct dhregs {
int dhcsr;
int dhnxch;
int dhlpr;
int dhcar;
int dhbcr;
int dhbar;
int dhbreak;
int dhsilo;
};
int dhaddrs[2] { 0760020, 0760040 };
#ifdef DNTTY
int dnidlflg; /* Set when idle timeout rtn activated */
#endif DNTTY
/*
* Open a DH11 line.
*/
dhopen(dev, flag)
{
register struct tty *tp;
register struct dhregs *dhaddr;
extern dhstart();
#ifdef DNTTY
extern dnidltmo();
if(dnidlflg == 0) {
dnidlflg++;
timeout(dnidltmo, 0, IDLTMO);
}
#endif DNTTY
if (dev.d_minor >= NDH11) {
u.u_error = ENXIO;
return;
}
dhaddr = dhaddrs[ dev.d_minor >> 4 ];
tp = &dh11[dev.d_minor];
tp->t_addr = dhstart;
tp->t_dev = dev;
dhaddr->dhcsr =| IENABLE;
tp->t_state =| WOPEN|SSTART;
if ((tp->t_state&ISOPEN) == 0) {
/* tp->t_erase = CERASE; */
/* tp->t_kill = CKILL; */
/* tp->t_quit = CQUIT; /* I'm not sure this is the best */
/* tp->t_intr = CINTR; /* place to set these up but... */
/* tp->t_EOT = CEOT; */
tp->t_speeds = SSPEED | (SSPEED<<8);
tp->t_flags = ODDP|EVENP|ECHO;
#ifdef DNTTY
tp->t_dnflgs = 0;
tp->t_ioct = 1;
/* DN: following line is complete crock to turn off modem
* control for the first DH. It would be unnecessary if
* there was some decent way to set TTY parameters BEFORE
* the open attempt, or if system itself knew more about
* its hardwired line parameters. Can just use null modems,
* but it would sure be nice to have things set right from
* the start.
*/
/* if(dev.d_minor>>4 == 0) tp->t_flags =| DUMC; */
/* another big crock */
if((dev.d_minor&017) < 5) tp->t_flags =| DUMC;
#endif DNTTY
dhparam(tp);
} else if (tp->t_state&XCLUDE && u.u_uid!=0) {
u.u_error = EBUSY;
return;
}
dmopen(dev);
ttyopen(dev, tp);
}
/*
* Close a DH11 line.
*/
dhclose(dev)
{
register struct tty *tp;
tp = &dh11[dev.d_minor];
if (tp->t_flags&HUPCL)
dmctl(dev, TURNOFF);
tp->t_state =& (CARR_ON|SSTART);
#ifdef DNTTY
tp->t_dnflgs =& ~(TMOCTS|TMORTS);
#endif DNTTY
wflushtty(tp);
}
/*
* Read from a DH11 line.
*/
dhread(dev)
{
ttread(&dh11[dev.d_minor]);
}
/*
* write on a DH11 line
*/
dhwrite(dev)
{
ttwrite(&dh11[dev.d_minor]);
}
/*
* DH11 receiver interrupt.
*/
dhrint(dev)
int dev;
{
register struct tty *tp;
register int c;
register struct dhregs *dhaddr;
dhaddr = dhaddrs[dev];
while ((c = dhaddr->dhnxch) < 0) { /* char. present */
tp = &dh11[ (dev<<4) | ((c>>8) & 017) ];
if (tp >= &dh11[NDH11])
continue;
if((tp->t_state&ISOPEN)==0) {
wakeup(tp);
continue;
}
if (c&PERROR)
if ((tp->t_flags&(EVENP|ODDP))==EVENP
|| (tp->t_flags&(EVENP|ODDP))==ODDP )
continue;
if (c&FRERROR) /* break */
if (tp->t_flags&RAW)
c = 0; /* null (for getty) */
else
c = tp->t_intr; /* intr */
#ifdef DNTTY
if (tp->t_dnflgs&BAUDOT){
tp->t_ioct++; /* Bump to mark non-idle */
c = bditab[(c&037)|(tp->t_dnflgs&FIGS ? 040 : 0)]&0377;
/* printf("(I:%o,%o)",ac&0177,c); /* Debug */
switch(c) {
case FIGSCHR: tp->t_dnflgs =| FIGS; continue;
case LETSCHR: tp->t_dnflgs =& ~FIGS; continue;
case 015:
if(tp->t_dnflgs&LICLF) break;
else {tp->t_dnflgs=| LICCR; continue;}
case 012:
if(tp->t_dnflgs&LICCR){c = 015; break;}
else {tp->t_dnflgs=| LICLF; continue;}
}
tp->t_dnflgs =& ~(LICCR | LICLF);
}
#endif DNTTY
ttyinput(c, tp);
}
}
/*
* stty/gtty for DH11
*/
dhsgtty(dev, av)
int *av;
{
register struct tty *tp;
register struct dhregs *dhaddr;
register int *mod;
tp = &dh11[dev.d_minor];
/*
* Special weirdness.
* On stty, if the input speed is 15 (EXT B)
* then the output speed selects a special function.
* The stored modes are not affected.
*/
if (av==0 && (mod=u.u_arg)[0].lobyte==15) {
dhaddr = dhaddrs[dev.d_minor>>4];
switch (mod[0].hibyte) {
/*
* Set break
*/
case 1:
dhaddr->dhbreak =| 1<<(dev.d_minor&017);
return;
/*
* Clear break
*/
case 2:
dhaddr->dhbreak =& ~(1<<(dev.d_minor&017));
return;
/*
* Turn on request to send
*/
case 3:
dmctl(dev, TURNON|RQS);
return;
/*
* Turn off request to send
*/
case 4:
dmctl(dev, TURNON);
return;
/*
* Prevent opens on channel
*/
case 5:
tp->t_state =| XCLUDE;
return;
}
return;
}
if (ttystty(tp, av))
return;
dhparam(tp);
}
/*
* Set parameters from open or stty into the DH hardware
* registers.
*/
dhparam(atp)
struct tty *atp;
{
register struct tty *tp;
register int lpr;
register struct dhregs *dhaddr;
tp = atp;
spl5();
dhaddr = dhaddrs[ tp->t_dev.d_minor >> 4 ];
dhaddr->dhcsr.lobyte = (tp->t_dev.d_minor&017) | IENABLE;
/*
* Hang up line?
*/
if (tp->t_speeds.lobyte==0) {
tp->t_flags =| HUPCL;
dmctl(tp->t_dev, TURNOFF);
return;
}
lpr = (tp->t_speeds.hibyte<<10) | (tp->t_speeds.lobyte<<6);
switch (tp->t_speeds.lobyte) {
#ifdef DNTTY
case BEXTA: /* DN: Crock - Ext A implies baudot */
lpr =| BITS5|TWOSB; /* 5 bits with 1.5 stop bits */
tp->t_dnflgs =| BAUDOT; /* State of FIGS undefined */
break;
#endif DNTTY
case B134: /* 134.5 baud */
lpr =| BITS6|PENABLE|HDUPLX;
break;
case B110: /* 110 baud */
lpr =| TWOSB; /* fall thru to continue */
default:
if (tp->t_flags&EVENP)
if (tp->t_flags&ODDP) lpr =| BITS8;
else lpr =| BITS7|PENABLE;
else lpr =| BITS7|OPAR|PENABLE;
}
dhaddr->dhlpr = lpr;
spl0();
}
/*
* DH11 transmitter interrupt.
* Restart each line which used to be active but has
* terminated transmission since the last interrupt.
*/
dhxint(dev)
int dev;
{
register struct tty *tp;
register ttybit, bar;
struct dhregs *dhaddr;
dhaddr = dhaddrs[dev];
bar = dhsar[dev] & ~dhaddr->dhbar;
dhaddr->dhcsr =& ~XINT;
ttybit = 1;
for (tp = &dh11[dev<<4]; bar; tp++) {
if(bar&ttybit) {
dhsar[dev] =& ~ttybit;
bar =& ~ttybit;
tp->t_state =& ~BUSY;
dhstart(tp);
}
ttybit =<< 1;
}
}
/*
* Start (restart) transmission on the given DH11 line.
*/
dhstart(atp)
struct tty *atp;
{
extern ttrstrt();
#ifdef DNTTY
extern dnrstrt(),dnrtsoff(); /* timeout routines */
#endif DNTTY
register c, nch;
register struct tty *tp;
int sps, dev;
struct dhregs *dhaddr;
char *cp;
sps = PS->integ;
spl5();
tp = atp;
dev = tp-dh11;
dhaddr = dhaddrs[ dev.d_minor >> 4 ];
/*
* If it's currently active, or delaying,
* no need to do anything.
*/
if (tp->t_state&(TIMEOUT|BUSY))
goto out;
/* Commented out; too MIT specific.
* It has been cvted for multi DH/DM tho. --KLH 5/15/79
*/
#ifdef COMMENT
/*
* This routine hacks our line printer. Namely it checks
* if the line printer is busy (dmsecrcv) and if it is,
* goes away for a while.
*/
if ((tp->t_flag2 & SECRX) && dmsecrcv(dev)) {
timeout(ttrstrt, tp, 60);
tp->t_state =| TIMEOUT;
goto out;
}
#endif COMMENT
/*
* t_char is a delay indicator which may have been
* left over from the last start.
* Arrange for the delay.
*/
if ((c = tp->t_char)
#ifdef DNTTY
&& ((c >= 0200) || (!(tp->t_dnflgs&BAUDOT)))
#endif DNTTY
) {
tp->t_char = 0;
timeout(ttrstrt, tp, (c&0177)+6);
tp->t_state =| TIMEOUT;
goto out;
}
#ifdef DNTTY
redo:
#endif DNTTY
cp = dh_clist[dev.d_minor];
nch = 0;
#ifdef DNTTY
if (tp->t_dnflgs&BAUDOT) {
if (!(tp->t_dnflgs&CTS)) { /* If can't send, */
if (dmaskcts(dev)) { /* ask politely */
tp->t_dnflgs =| CTS; /* Won! */
tp->t_dnflgs =& ~TMOCTS; /*may be pending*/
printf(">"); /* Debug crock */
}
else { /* Barf, wait a second. */
if((tp->t_dnflgs&TMOCTS) == 0) {
tp->t_dnflgs =| TMOCTS;
timeout(dnrstrt, tp, 60);
}
goto out;
}
}
if (c = tp->t_char) { /* Holds extra output char */
tp->t_char = 0;
*cp++ = c;
nch--;
}
}
#endif DNTTY
/*
* Copy DHNCH characters, or up to a delay indicator,
* to the DMA area.
*/
while (nch > -DHNCH && (c = getc(&tp->t_outq))>=0) {
if (c >= 0200 && (tp->t_flag2 & LITOUT)==0) {
tp->t_char = c;
break;
}
#ifdef DNTTY
if (tp->t_dnflgs&BAUDOT) {
if ((c = bdtcvt(c,tp)) < 0)
continue; /* May want to flush char */
*cp++ = c;
if ((--nch > -DHNCH) && tp->t_char) {
c = tp->t_char;
tp->t_char = 0;
} /* Drop thru to store */
else continue; /* Already stored char so skip */
}
#endif DNTTY
*cp++ = c;
nch--;
}
/*
* If the writer was sleeping on output overflow,
* wake him when low tide is reached.
*/
if (tp->t_outq.c_cc<=TTLOWAT && tp->t_state&ASLEEP) {
tp->t_state =& ~ASLEEP;
wakeup(&tp->t_outq);
}
/*
* If any characters were set up, start transmission;
* otherwise, check for possible delay.
*/
if (nch) {
dhaddr->dhcsr.lobyte = (dev.d_minor & 017) | IENABLE;
dhaddr->dhcar = cp+nch;
dhaddr->dhbcr = nch;
c = 1 << (dev.d_minor & 017);
dhaddr->dhbar =| c;
dhsar[dev.d_minor>>4] =|c;
tp->t_state =| BUSY;
#ifdef DNTTY
tp->t_dnflgs =& ~(TMOCTS);
tp->t_ioct++; /* Bump IO cnt to mark non-idle */
#endif DNTTY
} else if ((c = tp->t_char)
#ifdef DNTTY
&& ((c >= 0200) || (!(tp->t_dnflgs&BAUDOT)))
#endif DNTTY
) {
tp->t_char = 0;
timeout(ttrstrt, tp, (c&0177)+6);
tp->t_state =| TIMEOUT;
}
#ifdef DNTTY
/* This code contrives to turn off DN modem's RTS lead after
* the last char has left the DH. We cannot turn it off after
* the output-complete interrupt because 2 chars are still in the
* UART, and we can't pad out the delay because baudot terms
* have no padding character... so set up a demon to check things
* after 2 char times (2*(1/6 sec)) have passed.
*/
/* New additional crock: try to ensure that when output
* is done, tty is left in LETS mode rather than FIGS. No
* attempt at extra buffering or optimization is done here,
* because the speeds are so slow (6cps) it is highly probable
* that when the output queue becomes empty, the user program
* really has nothing more to send. We'll see.
*/
else if (tp->t_dnflgs&BAUDOT) {
if(tp->t_dnflgs&FIGS){ /* Crock */
tp->t_char = LETSCHR; /* " */
tp->t_dnflgs =& ~FIGS; /* " */
goto redo; /* " */
} /* " */
/* Now arrange to turn off RTS */
if(tp->t_dnflgs&TMORTS)
outtime(dnrtsoff, tp); /* Remove existing timeout*/
else tp->t_dnflgs =| TMORTS;
timeout(dnrtsoff, tp, VRTMO); /* after last char leaves DH */
}
#endif DNTTY
out:
PS->integ = sps;
}
/* ttymode handler for dh11 */
dhttymd(dev,acp)
int *acp;
{
register struct tty *tp;
tp = &dh11[dev.d_minor];
if (ttmode(tp, acp)) return;
dhparam(tp);
}
/*
* DN Baudot TTY support - output conversion
*/
#ifdef DNTTY
/* All ASCII transformations now done -- Hack baudot here
* if necessary (note LCASE should have done some work too)
*/
bdtcvt(ac,atp)
int ac;
struct tty *atp;
{
register struct tty *tp;
register int c;
tp = atp;
c = ac;
if (c >= 0140) c =& 0137; /* At this point, be merciless about case. */
if(c < 040) /* If a control char, check special cases */
switch (c) {
case '\r': c = 0210; break;
case '\n': c = 0202; break;
case 007: c = 053; break; /* ^G - bell */
case 006: c = FIGSCHR; break; /* ^F - force FIGS */
case 014: c = LETSCHR; break; /* ^L - force LETS */
default: return(-1);
}
else c = bdotab[c-040]&0377;
/* printf("(O:%o,%o)\n",ac,c); /* Debug */
if (c&0200) {
if(c==NOCH) c = 04; /* for now, cvt randoms to SP */
switch (c) {
case FIGSCHR: tp->t_dnflgs=|FIGS; break;
case LETSCHR: tp->t_dnflgs=& ~FIGS; break;
case 0202: /* Special crock for LF */
tp->t_char = LETSCHR&037;/* Follow with LETS */
tp->t_dnflgs=& ~FIGS;
break;
}
return(c&037);
}
if(c&040 && !(tp->t_dnflgs&FIGS)) { /* Wants FIGS and not in? */
tp->t_char = c&037; /* Store char */
tp->t_dnflgs =| FIGS;
return(FIGSCHR&037);
}
if(!(c&040) && tp->t_dnflgs&FIGS) { /* Wants LETS and not in? */
tp->t_char = c&037; /* Store char */
tp->t_dnflgs =& ~FIGS;
return(LETSCHR&037);
}
return(c&037);
}
#endif DNTTY
/*
* DN Baudot TTY support - timeout routines
*/
/* Both of these routines are called with spl = 5 from the clock
* interrupt routine, so need not worry about DH or DM interrupts.
* Likewise, they won't be executed if already at some int level,
* so DH/DM int level rtns don't need to worry about these either.
*/
#ifdef DNTTY
dnrstrt(atp)
struct tty *atp;
{
extern dnrstrt();
register struct tty *tp;
tp = atp;
printf("X"); /* Debug hack */
if (tp->t_dnflgs&TMOCTS) /* Still need CTS check? */
if((tp->t_state&ISOPEN) == 0) { /* For redundant safety */
tp->t_dnflgs =& ~TMOCTS;
return;
}
if(tp->t_state&BUSY || dmaskcts(tp->t_dev)) {
tp->t_dnflgs =& ~TMOCTS;
tp->t_dnflgs =| CTS;
dhstart(tp);
}
else timeout(dnrstrt, tp, 60);
}
dnrtsoff(atp)
struct tty *atp;
{
register struct tty *tp;
tp = atp;
if (tp->t_dnflgs&TMORTS) {
tp->t_dnflgs =& ~TMORTS;
if (!(tp->t_state&BUSY)) {
dmrtsoff(tp->t_dev);
tp->t_dnflgs =& ~CTS;
printf("O"); /* Debug hack */
}
}
}
#endif DNTTY
/*
* "outtime" - DN addition.
* The reverse of the "timeout" kernel routine; removes
* an entry from the clock queue.
* Called with the same params as timeout, except the third
* argument isn't furnished. If outtime finds an entry with
* the same argument and function, it will remove it.
* It is even safe to call this while the clock routine is
* hacking the queue; it checks for imminent execution and
* substitutes a null routine if so.
* It doesn't look for more than one entry, although it could.
* (remove the break and ensure p1 is preserved during entry flush).
*/
#ifdef DNTTY
#include "systm.h"
outtime(fun, arg)
{
extern nullrtn();
register struct callo *p1, *p2;
register a;
int s;
a = arg;
p1 = &callout[0];
s = PS->integ;
spl7();
while(p1->c_func != 0) {
if(p1->c_arg == a && p1->c_func == fun) {
if(p1->c_time <= 0) p1->c_func = &nullrtn;
else {
p2 = p1++;
while(p2->c_func = p1->c_func) {
p2->c_time = p1->c_time;
p2->c_arg = p1->c_arg;
p1++; p2++;
}
}
break;
}
p1++;
}
PS->integ = s;
}
nullrtn(dummy)
{
}
#endif DNTTY
/**/
/* Idle timeout routine -- called every IDLTMO ticks
* to see if any lines have been inactive too long & need hangup.
*/
#ifdef DNTTY
dnidltmo()
{ register i;
register struct tty *tp;
for (tp = &dh11[0]; tp < &dh11[NDH11]; tp++)
if(tp->t_dnflgs&BAUDOT)
{ if ((tp->t_state&CARR_ON) && (tp->t_ioct == 0))
dhclose(tp->t_dev);
tp->t_ioct = 0;
}
timeout(dnidltmo, 0, IDLTMO);
}
#endif DNTTY