# /* * 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