/* * Vdu driver -- local terminal on PALS * * - uses Autodriver Channel for output * */ #include "../h/local.h" #ifdef SCCS_ID static char SCCS_ID [] = "@(#)vdu.c 3.2 02:09:32 - 81/12/10 "; #endif SCCS_ID #include "../h/param.h" #include "../h/conf.h" #include "../h/tty.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/proc.h" #include "../h/systm.h" #include "../h/ccb.h" #include "../h/mx.h" int OBUF = 1; /*** Patch here to turn off ACD ***/ extern int nvdu; /* number of ports */ extern char vduaddr[]; /* port addresses */ extern char vdurate[][4]; /* Baud rate table */ extern struct tty vdu[]; /* common tty structure */ extern char consaddr; /* 'console' address */ extern char conscmd2; /* PALS command 2 for 'console' */ extern struct ccb *vduccb; /* output channel control blocks */ extern short isp[]; /* interrupt service ptr table */ int vdustart(), vdurint(), vduxint(), ttrstrt(); /* * PALS commands & status bits */ /* command 1 */ #define DIS 0200 #define EN 0100 #define DTR 0040 #define WRT 0002 #define CMD1 0001 /* command 2 */ #define CLKSHIFT 6 /* offset of CLOCK bits in command 2 */ #define DATA8 0060 /* 8 data bits */ #define DATA7 0040 /* 7 data bits */ #define STOP 0010 /* 2 stop bits */ #define PARE 0006 /* even parity */ /* Status */ #define PF 0100 #define FRERR 0040 #define BSY 0010 #define EXA 0004 #define CARR_OFF 0002 /* * open routine: * called each time a process opens a terminal as a character file * * - if the terminal was previously inactive, set up the initial status * and arm interrupts */ vduopen(dev) { register struct tty *tp; if ((minor(dev) >= nvdu) || ( minor(dev) >= MAXVDU )) { u.u_error = ENXIO; return; } tp = &vdu[minor(dev)]; if ((tp->t_state& ISOPEN) == 0) { tp->t_dev = dev; tp->t_state = ISOPEN; if (tp->t_flags == 0) tp->t_flags = EVENP | ODDP | XTABS | CRMOD | ECHO; ttychars(tp); tp->t_oproc = vdustart; isp[vduaddr[minor(dev)] + 1] = (short)&vduccb[minor(dev)] + 1; vduenab(tp); } spl4(); while (!(tp->t_state&CARR_ON)) sleep(&tp->t_rawq, TTIPRI); spl0(); if ((tp->t_state&XCLUDE) && u.u_uid != 0) { u.u_error = EBUSY; return; } (*linesw[tp->t_line].l_open)(dev, tp); } /* * close routine: * - called only when last process using terminal releases it */ vduclose(dev) { register struct tty *tp; tp = &vdu[minor(dev)]; (*linesw[tp->t_line].l_close)(tp); ttyclose(tp); vdudisab(dev); } /* * read, write, ioctl routines: * - call standard tty routines */ vduread(dev) { register struct tty *tp; tp = &vdu[minor(dev)]; (*linesw[tp->t_line].l_read)(tp); } vduwrite(dev) { register struct tty *tp; tp = &vdu[minor(dev)]; (*linesw[tp->t_line].l_write)(tp); } vduioctl(dev, cmd, addr, flag) caddr_t addr; { register struct tty *tp; tp = &vdu[minor(dev)]; if (ttioccom(cmd, tp, addr, dev) == 0) { u.u_error = ENOTTY; return; } /* Stty - reissue CMD 2 to change baud rate */ if (cmd == TIOCSETP || cmd == TIOCSETN) vduenab(tp); } /* * vdustart routine - called when there might be something to send * to the terminal */ vdustart(atp) struct tty *atp; { register struct tty *tp; register c, waddr; register struct ccb *ccb; tp = atp; waddr = vduaddr[tp->t_dev] + 1; trace(1<<16, "vdustart", waddr); if ((tp->t_state&(TIMEOUT|TTSTOP|BUSY|CARR_ON)) != CARR_ON || (c = getc(&tp->t_outq)) < 0) return; if (c > 0177 && (tp->t_flags&RAW) == 0) { tp->t_state |= TIMEOUT; timeout(ttrstrt, tp, c&0177); } else { tp->t_state |= BUSY; wd(waddr, c); trace(1<<16, "wrt", ss(waddr)); if (OBUF) { ccb = &vduccb[minor(tp->t_dev)]; if (vduputc(tp, ccb)) ccb->cc_ccw = 0x85; } } } vdustop(tp) register struct tty *tp; { register struct ccb *ccb; register n; trace(1<<16, "vdstop", tp->t_dev); if (!OBUF) return; ccb = &vduccb[minor(tp->t_dev)]; if ((n = ccb->cc_cnt1 + ccb->cc_cnt0 - 1) > 0) ndflush(&tp->t_outq, n); ccb->cc_cnt1 = 0; ccb->cc_ccw = 0; } /* * Set up ccb to continue output */ vduputc(tp, ccb) register struct tty *tp; register struct ccb *ccb; { register n; trace(1<<16, "vduputc", ccb->cc_buf1); if ((n = ccb->cc_cnt1 = ndqb(&tp->t_outq, 0200)) <= 0) return(0); n--; ccb->cc_cnt0 = -n; ccb->cc_buf0 = tp->t_outq.c_cf + n; trace(1<<16, "ccount", n); return(1); } vdurint(dev, stat) register stat; { register int raddr; register struct tty *tp; register char c; tp = &vdu[dev]; raddr = vduaddr[dev]; if (!(tp->t_state & ISOPEN)) return; trace(1<<16, "rint", raddr); trace(1<<16, "stat", stat); c = rd(raddr); trace(1<<16, "char", c); if (stat & CARR_OFF) { if (tp->t_state & CARR_ON) { if (tp->t_chan) scontrol(tp->t_chan, M_SIG, SIGHUP); else signal(tp->t_pgrp, SIGHUP); flushtty(tp); } tp->t_state &= ~CARR_ON; return; } if (!(tp->t_state & CARR_ON)) { tp->t_state |= CARR_ON; wakeup(&tp->t_rawq); return; } if (stat & EXA) { /* * Status of BSY|EXA means DSR dropped (e.g. busy signal from * printer) */ if (stat == (BSY|EXA)) return; /* * Status of FRERR means line-break (treat like NULL in raw * mode, DEL otherwise) or garbled character (ignore) */ if (stat&FRERR) { if (c != 0) return; if ((tp->t_flags&RAW) == 0) c = 0177; } } (*linesw[tp->t_line].l_rint)(c, tp); } vduxint(dev, stat) { register int waddr; register struct tty *tp; register struct ccb *ccb; register c; tp = &vdu[dev]; waddr = vduaddr[dev] + 1; if (!(tp->t_state & ISOPEN)) return; trace(1<<16, "wint", waddr); trace(1<<16, "stat", stat); if ((stat & ~0xff) == 0) { /* Immediate interrupt */ tp->t_state &= ~BUSY; (*linesw[tp->t_line].l_start)(tp); } else { /* Auto-driver 'channel' routine */ ccb = &vduccb[minor(tp->t_dev)]; ndflush(&tp->t_outq, ccb->cc_cnt1); if ((tp->t_state&TTSTOP) || vduputc(tp, ccb) == 0) ccb->cc_ccw = 0; } #ifdef UCB_TTHIWAT if ((tp->t_state&ASLEEP) && tp->t_outq.c_cc <= TTLOWAT(tp)) { #else UCB_TTHIWAT if ((tp->t_state&ASLEEP) && tp->t_outq.c_cc <= TTLOWAT) { #endif UCB_TTHIWAT tp->t_state &= ~ASLEEP; if (tp->t_chan) mcstart(tp->t_chan, (caddr_t)&tp->t_outq); else wakeup((caddr_t)&tp->t_outq); } } /* * Arm interrupts from PALS and set baud rate */ vduenab(atp) struct tty *atp; { register struct tty *tp; register radd; register char *rate; register int clk; register cmd2, stat; tp = atp; if (tp->t_ospeed == 0) cmd2 = conscmd2; else { cmd2 = conscmd2 & (03<t_dev)][0]; for (clk = 0; clk < 4; clk++) if (*rate++ == tp->t_ospeed) { cmd2 = clk << CLKSHIFT; break; } if ((tp->t_flags&RAW) == 0) cmd2 |= DATA7 | STOP | PARE; else cmd2 |= DATA8 | STOP; } radd = vduaddr[minor(tp->t_dev)]; oc(radd, cmd2); oc(radd, EN | DTR | CMD1); oc(radd + 1, EN | DTR | WRT | CMD1); stat = ss(radd); if ((stat&CARR_OFF) == 0) tp->t_state |= CARR_ON; trace(1<<16, "vduenab", stat); rd(radd); } /* * Disarm interrupts from PALS */ vdudisab(dev) { register radd, wadd; radd = vduaddr[minor(dev)]; wadd = radd + 1; oc(radd, DIS | CMD1); oc(wadd, DIS | WRT | CMD1); trace(1<<16, "vdudisab", ss(radd)); } /* * Print a character on console. * Attempts to save and restore device * status. * * The last MSGBUFS characters * are saved in msgbuf for inspection later. */ putchar(c) register c; { register s, timo; register waddr; waddr = consaddr | 01; if (c != '\0' && c != '\r' && c != 0177) { *msgbufp++ = c; if(msgbufp >= msgbufMSGBUFS) msgbufp = msgbuf; } /* * Set baud rate & disable interrupts */ oc(waddr, conscmd2); oc(waddr, DIS | WRT | DTR | CMD1); /* * If last char was a break or null, don't print */ /*** ??? if ((KLADDR->rbuf&0177) == 0) return; ***/ timo = 30000; /* * Try waiting for the console tty to come ready, * otherwise give up after a reasonable time. */ while(ss(waddr)&BSY) if(--timo == 0) break; if(c == 0) return; wd(consaddr, c); if(c == '\n') { putchar('\r'); /*** putchar(0177); putchar(0177); ***/ } putchar(0); /* * Try to restore previous status */ oc(waddr, EN | WRT | DTR | CMD1); }