etty.c Q H# /* Revision - New handshake handler */ /* * general TTY subroutines */ #include "param.h" #include "systm.h" #include "user.h" /*#include "userx.h"*/ #include "tty.h" #include "proc.h" /*#include "procx.h"*/ #include "inode.h" /*#include "inodex.h"*/ #include "file.h" /*#include "filex.h"*/ #include "reg.h" #include "conf.h" #define DLDELAY 4 /* * The actual structure of a clist block manipulated by * getc and putc (mch.s) */ struct cblock { struct cblock *c_next; char info[6]; }; /* Structure to select one char out of queue */ struct { char q_char; }; /* The character lists-- space for 6*NCLIST characters */ struct cblock cfree[NCLIST]; /* List head for unused character blocks. */ struct cblock *cfreelist; /* * structure of device registers for KL, DL, and DC * interfaces-- more particularly, those for which the * SSTART bit is off and can be treated by general routines * (that is, not DH). */ struct { int ttrcsr; int ttrbuf; int tttcsr; int tttbuf; }; /* * routine called on first teletype open. * establishes a process group for distribution * of quits and interrupts from the tty. */ ttyopen(dev, atp) struct tty *atp; { register struct proc *pp; register struct tty *tp; pp = u.u_procp; tp = atp; if(pp->p_pgrp == 0) { pp->p_pgrp = pp->p_pid; u.u_ttyp = tp; u.u_ttyd = dev; tp->t_pgrp = pp->p_pid; } if ((tp->t_state & ISOPEN) == 0) { tp->t_asize = TTACK; tp->t_enq = CENQ; tp->t_ack = CACK; tp->t_esc = CESC; tp->t_cntla = CSPEC; tp->t_freeze = CFREEZE; tp->t_thaw = CTHAW; tp->t_hold = CHOLD; tp->t_release = CREL; tp->t_retype = CRETYPE; tp->t_clear = CCLEAR; tp->t_silent = CSILENT; tp->t_atime = 60; tp->t_alwat = (tp->t_asize&0377) >> 1; } tp->t_state =& ~WOPEN; tp->t_state =| ISOPEN; } /* * The routine implementing the gtty system call. * Just call lower level routine and pass back values. */ gtty() { int v[3]; register *up, *vp; vp = v; sgtty(vp); if (u.u_error) return; up = u.u_arg[0]; suword(up, *vp++); suword(++up, *vp++); suword(++up, *vp++); } /* * The routine implementing the stty system call. * Read in values and call lower level. */ stty() { register int *up; up = u.u_arg[0]; u.u_arg[0] = fuword(up); u.u_arg[1] = fuword(++up); u.u_arg[2] = fuword(++up); sgtty(0); } /* * Stuff common to stty and gtty. * Check legality and switch out to individual * device routine. * v is 0 for stty; the parameters are taken from u.u_arg[]. * c is non-zero for gtty and is the place in which the device * routines place their information. */ sgtty(v) int *v; { register struct file *fp; register struct inode *ip; if ((fp = getf(u.u_ar0[R0])) == NULL) return; ip = fp->f_inode; if ((ip->i_mode&IFMT) != IFCHR) { u.u_error = ENOTTY; return; } (*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], v); } /* * Wait for output to drain, then flush input waiting. */ wflushtty(atp) struct tty *atp; { register struct tty *tp; tp = atp; spl5(); while (tp->t_outq.c_cc) { tp->t_state =| ASLEEP; sleep(&tp->t_outq, TTOPRI); } flushtty(tp); spl0(); } /* * Initialize clist by freeing all character blocks, then count * number of character devices. (Once-only routine) */ cinit() { register int ccp; register struct cblock *cp; register struct cdevsw *cdp; ccp = cfree; for (cp=(ccp+07)&~07; cp <= &cfree[NCLIST-1]; cp++) { cp->c_next = cfreelist; cfreelist = cp; } ccp = 0; for(cdp = cdevsw; cdp->d_open; cdp++) ccp++; nchrdev = ccp; } /* * flush all TTY queues */ flushtty(atp) struct tty *atp; { register struct tty *tp; register int sps; sps = PS->integ; spl5(); tp = atp; while (getc(&tp->t_canq) >= 0); while (getc(&tp->t_outq) >= 0); wakeup(&tp->t_canq); wakeup(&tp->t_outq); PS->integ = sps; } /* * Place a character on can TTY input queue, putting in delimiters * and waking up top half as needed. * Also echo if required. * The arguments are the character and the appropriate * tty structure. */ /* Echo places a character on the appropriate tty's output queue, and starts * it up. Also, some processing to output certain control characters is done */ echo(ac,atp) register struct tty *atp; { if (ac < 0 || ((atp->t_flags & ECHO) == 0)) return; if (ac <= 037 && (ac < 07 || ac > 012)) { echo ('^',atp); ac =+ 0100; } ttyoutput(ac,atp); ttstart(atp); } echostr(s,tp) register char *s; register struct tty *tp; { if ((tp->t_flags & ECHO) == 0) return; while(*s) putc(*s++,&tp->t_outq); ttstart(tp); } ttcursor(x,y,tp) register struct tty *tp; { if ((tp->t_flags & ECHO) == 0) return; echostr("\033Y",tp); /* escape sequence */ putc(x+040,&tp->t_outq); /* row */ putc(y+040,&tp->t_outq); /* col */ putc(0201,&tp->t_outq); /* vt52 delay */ ttstart(tp); tp->t_col = y; } ttretype(tp) register struct tty *tp; { register cptr; cptr = tp->t_llf; /* start from end of last line */ /* if ((cptr = tp->t_llf) == 0) cptr = tp->t_canq.c_cf; */ if (tp->t_canq.c_cc) while (cptr != tp->t_canq.c_cl) { if ((cptr & 07) == 0) if ( (cptr = &((cptr - 010)->c_next->info[0])) == tp->t_canq.c_cl ) break; echo(cptr->q_char,tp); cptr++; } } char *tterase "\b \b"; char *ttclear "\033H\033J"; char *ttforw "\033C"; char *ttkill "\033K"; ttyinput(ac,atp) struct tty *atp; { register int t_flags, c; register struct tty *tp; int cptr; int cano; /* type of terminal */ /* tk_nin =+ 1;*/ tp = atp; c=ac&0377; t_flags = tp->t_flags; /* Load registers */ if ((tp->t_flag2 & LITIN) == 0) c =& 0177; if (tp->t_flag2 & (RACK | WACK)) { if ((tp->t_flag2 & WACK) && (c == (tp->t_ack & 0377))) { tp->t_acnt = tp->t_asize; wakeup(&tp->t_acnt); return; } if ((tp->t_flag2 & RACK) && (c == (tp->t_enq & 0377))) { if ((tp->t_canq.c_cc <= tp->t_alwat) || /* ack if possible */ (tp->t_llf == tp->t_canq.c_cf)) { putc(tp->t_ack,&tp->t_outq); ttstart(tp); } else tp->t_state =| ENQRCV; return; } if (tp->t_state & ESCRCV) { tp->t_state =& ~ESCRCV; switch(c) { case CESC1: c = tp->t_enq; break; case CESC2: c = tp->t_ack; break; } } else if (c == (tp->t_esc & 0377)) { tp->t_state =| ESCRCV; return; } } if ((tp->t_flag2 & WHOLD) && !(tp->t_state & HELD) && (tp->t_canq.c_cc >= tp->t_asize)) { /* send a HOLD character */ tp->t_state =| HELD; putc(tp->t_hold, &tp->t_outq); ttstart(tp); } if (tp->t_flag2 & LITIN) /* Literal input */ { if (tp->t_canq.c_cc >= TTYHOG) return; putc(c,&tp->t_canq); tp->t_llf = tp->t_canq.c_cl; /* set llf */ wakeup(&tp->t_canq); /* wake up!! */ return; } if (t_flags & CRMOD) if (c == '\r') c = '\n'; /* following misfeature flushed 5/15/79 --KLH else if (c == '\n') c = '\r' */ if (((c==tp->t_intr) || (c==tp->t_quit) || (c==tp->t_cntla)) && ((tp->t_flag2 & EEI) || ((t_flags & RAW) == 0))) { if (c == tp->t_intr) signal (tp->t_pgrp,SIGINT); else signal (tp->t_pgrp, c==tp->t_quit?SIGQIT:SIGSPC); flushtty (tp); tp->t_state =& ~FROZEN; /* signals thaw tty */ echo(c,tp); echo('\n',tp); return; } if (tp->t_canq.c_cc >= TTYHOG) { if (t_flags & RAW) return; if ((c != tp->t_erase) && (c != tp->t_kill)) return; } switch(tp->t_cano) { case TC_GT40: case TC_TERM: case TC_VT52: cano = 1; break; default: cano = 0; break; } if (t_flags & LCASE && c >= 'A' && c <= 'Z') /* Convert to lower case if necessary */ c =+ 'a' - 'A' ; if (t_flags & RAW) { putc(c,&tp->t_canq); } else if (c == tp->t_erase) { c = -1; if ((tp->t_llf != tp->t_canq.c_cl ) && ((c = unputc(&tp->t_canq)) >= 0)) if (cano) { /* special terminals */ switch (partab[c] & 077) { case 1: /* non - printing */ if (c <= 012 && c >= 07) break; case 5: case 6: echostr(tterase,tp); tp->t_col--; case 0: /* normal */ echostr(tterase,tp); tp->t_col--; break; case 2: echostr(ttforw,tp); tp->t_col++; break; case 4: ttcursor(0136,tp->t_delpos,tp); ttretype(tp); break; } c = -1; /* normal printing terminal delete */ } else if ((tp->t_state & ERMODE) == 0) { echo('\\',tp); tp->t_state =| ERMODE; } } else { if (tp->t_state & ERMODE) /* If in erase mode, */ { echo('\\',tp); /* echo '\' to indicate out of erase mode */ tp->t_state =& ~ERMODE; /* and turn it off */ } if (c == tp->t_retype) { if (cano) ttcursor(0136,tp->t_delpos,tp); else { echo(c,tp); echo('\n',tp); } ttretype(tp); c = -1; } else if (c == tp->t_kill) { if (cano) { ttcursor(0136,tp->t_delpos,tp); echostr(ttkill,tp); } else { echo (c,atp); echo('\n',tp); } while ((tp->t_llf != tp->t_canq.c_cl) && (unputc(&tp->t_canq) > 0)) ; /* flush till empty or end-of-line */ c = -1; } else if (c == tp->t_EOT) /* End of file indicator */ { putc(c,&tp->t_canq); c = -2; } else if (c == tp->t_silent) { flushtty(tp); echo (c,tp); echo ('\n',tp); echo ('\n',tp); if (tp->t_flag2 & SILENT) tp->t_flag2 =& ~SILENT; else tp->t_flag2 =| SILENT; } else if (c == tp->t_clear) { if (cano) echostr(ttclear,tp); else { echo(c,tp); echo('\n',tp); } ttretype(tp); tp->t_delpos = 0; c = -1; } else if (c == tp->t_freeze) { tp->t_state =| FROZEN; /* freeze the screen */ return; } else if (c == tp->t_thaw) { tp->t_state =& ~FROZEN; /* wakeup output */ wakeup(&tp->t_outq); return; } else putc(c,&tp->t_canq); /* Anything else goes straight to can queue */ } echo(c,tp); /* Echo the character */ if (tp->t_canq.c_cc == 1 || (tp->t_llf&07) == 0) tp->t_llf = tp->t_canq.c_cf; /* Initialize t_llf */ if (t_flags & RAW || c == '\n' || c == -2) /* If line delimiter, wake up canan() */ { tp->t_delpos = 0; /* for kill processing */ tp->t_llf = tp->t_canq.c_cl; wakeup(&tp->t_canq); } return; /* (Implied return anyway) */ } /* * put character on TTY output queue, adding delays, * expanding tabs, and handling the CR/NL bit. * It is called both from the top half for output, and from * interrupt level for echoing. * The arguments are the character and the tty structure. */ ttyoutput(ac, tp) struct tty *tp; { register int c; register struct tty *rtp; register char *colp; int ctype,sps; /* tk_nout =+ 1;*/ rtp = tp; c = ac; if (rtp->t_flag2 & SILENT) return(0); if ((rtp->t_flag2 & (RACK | WACK)) && ((c == tp->t_enq) || (c == tp->t_ack) || (c == tp->t_esc))) { if (putc(rtp->t_esc,&rtp->t_outq)) return(1); if (c == rtp->t_enq) c = CESC1; else if (c == rtp->t_ack) c = CESC2; } if (rtp->t_flag2 & LITOUT) /* Literal output */ { if (putc(c, &rtp->t_outq)) return(1); else { if (rtp->t_acnt) rtp->t_acnt--; return(0); } } c =& 0177; /* * Ignore EOT in normal mode to avoid hanging up * certain terminals. */ if (c==004 && (rtp->t_flags&(RAW|WACK|RACK))==0) return(0); /* * Turn tabs to spaces as required */ if (c=='\t' && rtp->t_flags&XTABS) { do if (ttyoutput(' ', rtp)) return(1); while (rtp->t_col&07); return(0); } /* * for upper-case-only terminals, * generate escapes. */ if (rtp->t_flags&LCASE) { colp = "({)}!|^~'`"; while(*colp++) if(c == *colp++) { if (ttyoutput('\\', rtp)) return(1); c = colp[-2]; break; } if ('a'<=c && c<='z') c =+ 'A' - 'a'; } /* * turn <nl> to <cr><lf> if desired. */ if (c=='\n') if (ttyoutput('\r', rtp)) return(1); if (putc(c, &rtp->t_outq)) return(1); /* put char in buffer */ if (rtp->t_acnt) rtp->t_acnt--; /* * Calculate delays. * The numbers here represent clock ticks * and are not necessarily optimal for all terminals. * The delays are indicated by characters above 0200, * thus (unfortunately) restricting the transmission * path to 7 bits. */ colp = &rtp->t_col; ctype = partab[c]; c = 0; switch (ctype&077) { /* ordinary */ case 0: (*colp)++; /* non-printing */ case 1: break; /* backspace */ case 2: if (*colp) (*colp)--; break; /* newline */ case 3: ctype = (rtp->t_flags >> 8) & 03; if(ctype == 1) { /* tty 37 */ if (*colp) c = max((*colp>>4) + 3, 6); } else if(ctype == 2) { /* vt05 */ c = 6; } *colp = 0; break; /* tab */ case 4: ctype = (rtp->t_flags >> 10) & 03; if(ctype == 1) { /* tty 37 */ c = 1 - (*colp | ~07); if(c < 5) c = 0; } *colp =| 07; (*colp)++; break; /* vertical motion */ case 5: if(rtp->t_flags & VTDELAY) /* tty 37 */ c = 0177; break; /* carriage return */ case 6: ctype = (rtp->t_flags >> 12) & 03; if(ctype == 1) { /* tn 300 */ c = 5; } else if(ctype == 2) { /* ti 700 */ c = 10; } *colp = 0; } if(c) putc(c|0200, &rtp->t_outq); /* put in delay unless zero */ return(0); /* we always return 0 here because if inserting the delay char fails, we can just ignore. */ } /* * Restart typewriter output following a delay * timeout. * The name of the routine is passed to the timeout * subroutine and it is called during a clock interrupt. */ ttrstrt(atp) { register struct tty *tp; tp = atp; tp->t_state =& ~TIMEOUT; ttstart(tp); } /* * Start output on the typewriter. It is used from the top half * after some characters have been put on the output queue, * from the interrupt routine to transmit the next * character, and after a timeout has finished. * If the SSTART bit is off for the tty the work is done here, * using the protocol of the single-line interfaces (KL, DL, DC); * otherwise the address word of the tty structure is * taken to be the name of the device-dependent startup routine. */ ttstart(atp) struct tty *atp; { register int *addr, c; int sps; register struct tty *tp; struct { int (*func)(); }; tp = atp; addr = tp->t_addr; if (tp->t_state&SSTART) { (*addr.func)(tp); return; } sps = PS->integ; spl5(); if ((addr->tttcsr&DONE)==0 || tp->t_state&TIMEOUT) return; if ((c=getc(&tp->t_outq)) >= 0) { if (tp->t_flag2 & LITOUT) /* Literal output */ addr->tttbuf = c; else if (c<=0177) addr->tttbuf = c | (partab[c]&0200); else { timeout(ttrstrt, tp, (c&0177) + DLDELAY); tp->t_state =| TIMEOUT; } } PS->integ = sps; } /* * Called from device's read routine after it has * calculated the tty-structure given as argument. * The pc is backed up for the duration of this call. * In case of a caught interrupt, an RTI will re-execute. */ ttread(atp) struct tty *atp; { register struct tty *tp; register int c; tp = atp; tp->t_flag2 =& ~SILENT; spl5(); while ((tp->t_canq.c_cc == 0) || (tp->t_llf == tp->t_canq.c_cf)) { if ((tp->t_state & CARR_ON) == 0) return; sleep(&tp->t_canq,TTIPRI); } spl0(); while ((tp->t_canq.c_cf) && (tp->t_llf != tp->t_canq.c_cf)) { c = getc (&tp->t_canq); if (tp->t_canq.c_cc <= tp->t_alwat) { if ((tp->t_flag2 & RACK) && (tp->t_state & ENQRCV)) { tp->t_state =& ~ENQRCV; while (putc(tp->t_ack, &tp->t_outq)) sleep(&lbolt,22); ttstart(tp); } if ((tp->t_flag2 & WHOLD) && (tp->t_state & HELD)) { tp ->t_state =& ~HELD; while (putc(tp->t_release, &tp->t_outq)) sleep(&lbolt,22); ttstart(tp); } } if (tp->t_flags & RAW) { passc(c); break; } if ((c == tp->t_EOT) || passc(c) < 0 || c == '\n') break; } } /* * wakes up process waiting for an acknowledge if the * ACKWAIT bit in t_state is still set. */ ttywakeup(atp) register struct tty *atp; { if (atp->t_state & ACKWAIT) { atp->t_state =& ~ACKWAIT; wakeup(&atp->t_acnt); } } /* * Called from the device's write routine after it has * calculated the tty-structure given as argument. */ ttwrite(atp) struct tty *atp; { register struct tty *tp; register int c; tp = atp; if ((tp->t_state&CARR_ON)==0) return; while ((c=cpass())>=0) { spl5(); while(tp->t_state & FROZEN) sleep(&tp->t_outq, TTOPRI); /* wait for thaw */ while (tp->t_outq.c_cc > TTHIWAT) { ttstart(tp); tp->t_state =| ASLEEP; sleep(&tp->t_outq, TTOPRI); } while((tp->t_flag2 & WACK) && (tp->t_acnt == 0)) { while (putc(tp->t_enq, &tp->t_outq)) sleep(&lbolt,22); ttstart(tp); if ((tp->t_state & ACKWAIT) == 0) { timeout(ttywakeup,tp,tp->t_atime); tp->t_state =| ACKWAIT; } sleep(&tp->t_acnt,TTOPRI); } spl0(); while (ttyoutput(c, tp)) sleep(&lbolt, 21); } tp->t_delpos = tp->t_col; ttstart(tp); } /* * Common code for gtty and stty functions on typewriters. * If v is non-zero then gtty is being done and information is * passed back therein; * if it is zero stty is being done and the input information is in the * u_arg array. */ ttystty(atp, av) int *atp, *av; { register *tp, *v; tp = atp; if(v = av) { *v++ = tp->t_speeds; v->lobyte = tp->t_erase; v->hibyte = tp->t_kill; v[1] = tp->t_flags; return(1); } wflushtty(tp); v = u.u_arg; tp->t_speeds = *v++; tp->t_erase = v->lobyte; tp->t_kill = v->hibyte; tp->t_flags = v[1]; return(0); } /* common code for ttymode handlers */ ttmode(atp,acp) int *acp; char atp[]; /* Rather than considering atp as a pointer to a tty structure, it is treated * as an array of bytes, so that the individual bytes can be referenced as an * offset from the base, rather than being individually selected via ->. */ { register bn,bits; register int *cp; cp = acp; bits = (*cp) & TBITS; /* extract argument bits */ if ((bn = (((*cp)&TBYTNUM)>>8)) > TMAXBN) /* extract byte number, and check for validity */ { u.u_error = EINVAL; /* invalid argument */ return(1); } if ((*cp) & TFLUSH) wflushtty(atp); /* request to flush buffer first*/ *cp = ((*cp) & 0177400) + (atp[TFIXOFS+bn] & 0377); /* current value placed in *cp */ switch((*cp) & TCNTL) /* control bits */ { case TCLEAR: /* bit clear */ atp[TFIXOFS + bn] =& ~bits; break; case TSET: /* bit set */ atp[TFIXOFS + bn] =| bits; break; case TREPLAC: atp[TFIXOFS + bn] = bits; /* replace */ case TGET: ; /* just get it. (done anyway) */ } if (((*cp)&TCNTL != TGET) && (bn<6)) /* if speeds or mode changed, return 0. (for dhttymd) */ return(0); return(1); } )dh.c 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){c = 012; break;} else {tp->t_dnflgs=| LICCR; continue;} case 012: if(tp->t_dnflgs&LICCR) 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 dhdm.c % 3# /* * DM-BB driver * * This driver has been modified to support multiple DM11s, * in order to avail yourself of it you must do the * following. * * In this module: * Initialize the array dmaddrs to contain the base * addresses of your DM11s. * * In l.s: * When you set up the interrupt vectors for the * DM11s, you should add a DM11 index number to the * interrupt PS for each vector (0 for the first * one, 1 for the second, etc.) This index will be * passed to the interrupt routine so that it can * tell which DM11 caused the interrupt. This is * done so that one interrupt routine can service * the interrupts from all of the DM11s. * * globals: * dmaddrs array of pointers to the sets of dm11 registers. * * history: * * Started out as a standard release 6 DM11 driver. * Modified for Multi DM support by Dennis L. Mumaugh * 15 April 1977 * Thanks to Mark Kampe (UCLA) for assistance. */ #include "param.h" #include "tty.h" #include "conf.h" int dmaddrs[2] {0170500, 0170510}; struct tty dh11[]; int ndh11; /* Set by dh.c to number of lines */ #define BUSY 020 /* for CSR, not line status reg */ #define SECRX 020 #define DONE 0200 #define SCENABL 040 #define CLSCAN 01000 #define TURNON 03 /* CD lead, line enable */ #define TURNOFF 1 /* line enable only */ #define CARRIER 0100 #ifdef DNTTY #define DMRTS 04 #define DMCTS 040 #endif DNTTY struct dmregs { int dmcsr; int dmlstat; }; /* * Turn on the line associated with the (DH) device dev. */ dmopen(dev) { register struct tty *tp; register struct dmregs *dmaddr; dmaddr = dmaddrs[dev.d_minor >> 4]; tp = &dh11[dev.d_minor]; dmaddr->dmcsr = dev.d_minor&017; if (tp->t_flags&DUMC){ /* KLH: if don't want modem ctl, */ dmaddr->dmlstat = 0; /* turn it off for line */ tp->t_state =| CARR_ON; /* pretend carrier's on */ dmaddr->dmcsr = IENABLE|SCENABL; return; } dmaddr->dmlstat = TURNON; if (dmaddr->dmlstat&CARRIER) tp->t_state =| CARR_ON; dmaddr->dmcsr = IENABLE|SCENABL; spl5(); while ((tp->t_state&CARR_ON)==0) sleep(&tp->t_canq, TTIPRI); spl0(); } /* * If a DH line has the HUPCL mode, * turn off carrier when it is closed. */ /* This routine is obsoleted by existence of DMCTL. --KLH 5/15/79 * dmclose(dev) * { * register struct tty *tp; * register struct dmregs *dmaddr; * * dmaddr = dmaddrs[dev.d_minor>>4]; * tp = &dh11[dev.d_minor]; * if (tp->t_flags&HUPCL) { * dmaddr->dmcsr = dev.d_minor&017; * dmaddr->dmlstat = TURNOFF; * dmaddr->dmcsr = IENABLE|SCENABL; * } * } */ /* * Dump control bits into the DM registers. */ dmctl(dev, bits) { register struct dmregs *dmaddr; dmaddr = dmaddrs[dev.d_minor>>4]; dmaddr->dmcsr = dev.d_minor&017; dmaddr->dmlstat = bits; dmaddr->dmcsr = IENABLE|SCENABL; } /* * DM11 interrupt. * Mainly, deal with carrier transitions. */ dmint(dev) int dev; { register struct tty *tp; register struct dmregs *dmaddr; dmaddr = dmaddrs[dev]; if (dmaddr->dmcsr&DONE) { tp = &dh11[ (dev<<4) | (dmaddr->dmcsr&017) ]; if (tp < &dh11[ndh11]) { #ifdef DNTTY if (dmaddr->dmlstat&DMCTS) tp->t_dnflgs =| CTS; else tp->t_dnflgs =& ~CTS; #endif DNTTY wakeup(tp); if ((dmaddr->dmlstat&CARRIER)==0) { if ((tp->t_state&WOPEN)==0) { signal(tp->t_pgrp, SIGHUP); dmaddr->dmlstat = 0; flushtty(tp); } tp->t_state =& ~CARR_ON; } else tp->t_state =| CARR_ON; } dmaddr->dmcsr = IENABLE|SCENABL; } } /* This is the MIT routine "dmsecrcv" which isn't needed, hence * commented out. It has been converted for multiple DH/DM's however. * --KLH 5/15/79 */ #ifdef COMMENT dmsecrcv(dev) int dev; { register int ans; register struct dmregs *dmaddr; dmaddr = dmaddrs[dev.d_minor>>4]; dmaddr->dmcsr =& ~SCENABL; while (dmaddr->dmcsr & BUSY); dmaddr->dmcsr =& ~017; dmaddr->dmcsr =| dev.d_minor&017; dmaddr->dmlstat =| TURNOFF; ans = dmaddr->dmlstat & SECRX; dmaddr->dmcsr =| SCENABL; return(ans); } #endif COMMENT /* * DN Modem support */ #ifdef DNTTY /* Assert RTS for line and return state of CTS lead after suitable * delay for modem signal propagation. * This is called only from dhstart at spl 5, so needn't worry about * DH or timeout interrupts. */ dmaskcts(dev) int dev; { register struct dmregs *dmaddr; register int flag; dmaddr = dmaddrs[dev.d_minor>>4]; dmaddr->dmcsr = dev.d_minor&017; dmaddr->dmlstat =| DMRTS; flag = dmaddr->dmlstat&DMCTS; dmaddr->dmcsr = IENABLE | SCENABL; return(flag); } /* Turn off RTS for specified line */ dmrtsoff(dev) int dev; { register struct dmregs *dmaddr; dmaddr = dmaddrs[dev.d_minor>>4]; dmaddr->dmcsr = dev.d_minor&017; dmaddr->dmlstat =& ~DMRTS; dmaddr->dmcsr = IENABLE | SCENABL; } #endif DNTTY *dhfdm.c d # /* */ /* * DM-BB fake driver */ #include "tty.h" #include "conf.h" struct tty dh11[]; dmopen(dev) { register struct tty *tp; tp = &dh11[dev.d_minor]; tp->t_state =| CARR_ON; } dmclose(dev) { }