sheppard@parsec.UUCP (12/16/83)
#N:parsec:42500013:000:20063 parsec!sheppard Dec 15 16:10:00 1983 This version of the dmf driver is based on version 4.16, date 82/12/17, Berkeley 4.1c. It fixes ports 0 and 1 to where they are usable without a full set of RS232 lines. The fixes actually appeared in an earlier version of dmf.c, 4.3, which was posted to this group back in September by sbcs!perry. All I did was to incorporate them into a later (better?) version of the driver. Dtr and dsr (pins 6 and 20) must still be jumpered in the connector that plugs into the dmf distribution panel for it to work. We've been using it for some time now, and it appears to work well. Andy Sheppard Parsec Scientific Computer Corporation ----------------------- cut on the dotted line ------------------------- /* dmf.c 4.16 82/12/17 */ #include "dmf.h" #if NDMF > 0 /* * DMF32 driver * * TODO: * test with modem * load as much as possible into silo * get correct numbers for receive silo parameter timeout * use auto XON/XOFF * test reset code * optimize for efficient DMA and dynamically * decide between silo and DMA mode * * Modified by A. Sheppard, Parsec Scientific Computer Corporation * to work correctly on "modem" ports (0, 1) without a full set of * RS232 lines. Original fixes by RJ Spanbauer, SUNY/Stony Brook. * * Remember: dtr and dsr (pins 6 and 20) must be jumpered in order * for this to work. */ #include "../machine/pte.h" #include "bk.h" #include "../h/param.h" #include "../h/conf.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/ioctl.h" #include "../h/tty.h" #include "../h/map.h" #include "../h/buf.h" #include "../h/vm.h" #include "../h/bk.h" #include "../h/clist.h" #include "../h/file.h" #include "../h/uio.h" #include "../vaxuba/ubareg.h" #include "../vaxuba/ubavar.h" /* * Definition of the driver for the auto-configuration program. */ int dmfprobe(), dmfattach(), dmfrint(), dmfxint(); struct uba_device *dmfinfo[NDMF]; u_short dmfstd[] = { 0 }; struct uba_driver dmfdriver = { dmfprobe, 0, dmfattach, 0, dmfstd, "dmf", dmfinfo }; /* * In this driver, "dmf" (unqualified) refers to the async portion * of the dmf32, "dmfc" to the combo portion, "dmfs" to the sync * portion, "dmfl" to the lp portion, and "dmfd" to the dr portion. */ struct dmfdevice { short dmfccsr0; /* combo csr 0 */ short dmfccsr1; /* combo csr 1 */ short dmfs[4]; short dmfcsr; /* control-status register */ short dmflpr; /* line parameter register */ short dmfrbuf; /* receiver buffer (ro) */ union { u_short dmfirw; /* indirect register word */ u_char dmfirc[2]; /* " " bytes */ } dmfun; short dmfl[2]; short dmfd[4]; }; #define dmfrsp dmfrbuf /* receive silo parameter register (wo) */ #define dmftbuf dmfun.dmfirc[0] /* transmit buffer */ #define dmftsc dmfun.dmfirc[0] /* transmit silo count */ #define dmfrms dmfun.dmfirc[1] /* receive modem status */ #define dmflcr dmfun.dmfirc[0] /* line control register */ #define dmftms dmfun.dmfirc[1] /* transmit modem status */ #define dmftba dmfun.dmfirw /* transmit buffer address */ #define dmftcc dmfun.dmfirw /* transmit character count */ /* bits in dmfcsr */ #define DMF_TI 0100000 /* transmit interrupt */ #define DMF_TIE 0040000 /* transmit interrupt enable */ #define DMF_NXM 0020000 /* non-existant memory */ #define DMF_LIN 0003400 /* transmit line number */ #define DMF_RI 0000200 /* receiver interrupt */ #define DMF_RIE 0000100 /* receiver interrupt enable */ #define DMF_CLR 0000040 /* master reset */ #define DMF_IAD 0000037 /* indirect address register */ #define DMFIR_TBUF 000 /* select tbuf indirect register */ #define DMFIR_LCR 010 /* select lcr indirect register */ #define DMFIR_TBA 020 /* select tba indirect register */ #define DMFIR_TCC 030 /* select tcc indirect register */ /* bits in dmflpr */ #define BITS6 (01<<3) #define BITS7 (02<<3) #define BITS8 (03<<3) #define TWOSB 0200 #define PENABLE 040 /* DEC manuals incorrectly say this bit causes generation of even parity. */ #define OPAR 0100 #define DMF_IE (DMF_TIE|DMF_RIE) #define DMF_SILOCNT 32 /* size of DMF output silo (per line) */ /* bits in dmfrbuf */ #define DMF_DSC 0004000 /* data set change */ #define DMF_PE 0010000 /* parity error */ #define DMF_FE 0020000 /* framing error */ #define DMF_DO 0040000 /* data overrun */ /* bits in dmfrms */ #define DMF_USRR 0004 /* user modem signal (pin 25) */ #define DMF_SR 0010 /* secondary receive */ #define DMF_CTS 0020 /* clear to send */ #define DMF_CAR 0040 /* carrier detect */ #define DMF_RNG 0100 /* ring */ #define DMF_DSR 0200 /* data set ready */ /* bits in dmftms */ #define DMF_USRW 0001 /* user modem signal (pin 18) */ #define DMF_DTR 0002 /* data terminal ready */ #define DMF_RATE 0004 /* data signal rate select */ #define DMF_ST 0010 /* secondary transmit */ #define DMF_RTS 0020 /* request to send */ #define DMF_BRK 0040 /* pseudo break bit */ #define DMF_PREEMPT 0200 /* preempt output */ /* flags for modem control */ #define DMF_ON (DMF_DTR|DMF_RTS) #define DMF_OFF 0 /* bits in dmflcr */ #define DMF_MIE 0040 /* modem interrupt enable */ #define DMF_FLUSH 0020 /* flush transmit silo */ #define DMF_RBRK 0010 /* real break bit */ #define DMF_RE 0004 /* receive enable */ #define DMF_AUTOX 0002 /* auto XON/XOFF */ #define DMF_TE 0001 /* transmit enable */ #define DMFLCR_ENA (DMF_RE|DMF_TE) /* bits in dm lsr, copied from dh.c */ #define DML_USR 0001000 /* usr modem sig, not a real DM bit */ #define DML_DSR 0000400 /* data set ready, not a real DM bit */ #define DML_RNG 0000200 /* ring */ #define DML_CAR 0000100 /* carrier detect */ #define DML_CTS 0000040 /* clear to send */ #define DML_SR 0000020 /* secondary receive */ #define DML_ST 0000010 /* secondary transmit */ #define DML_RTS 0000004 /* request to send */ #define DML_DTR 0000002 /* data terminal ready */ #define DML_LE 0000001 /* line enable */ /* * Local variables for the driver */ char dmf_speeds[] = { 0, 0, 1, 2, 3, 4, 0, 5, 6, 7, 010, 012, 014, 016, 017, 0 }; struct tty dmf_tty[NDMF*8]; char dmfsoftCAR[NDMF]; #ifndef lint int ndmf = NDMF*8; /* used by iostat */ #endif int dmfact; /* mask of active dmf's */ int dmfstart(), ttrstrt(); #ifdef DMFDMA /* * The clist space is mapped by the driver onto each UNIBUS. * The UBACVT macro converts a clist space address for unibus uban * into an i/o space address for the DMA routine. */ int dmf_ubinfo[MAXNUBA]; /* info about allocated unibus map */ static int cbase[MAXNUBA]; /* base address in unibus map */ #define UBACVT(x, uban) (cbase[uban] + ((x)-(char *)cfree)) #endif /* * Routine for configuration to set dmf interrupt. */ /*ARGSUSED*/ dmfprobe(reg, ctlr) caddr_t reg; int ctlr; { register int br, cvec; /* these are ``value-result'' */ register struct dmfdevice *dmfaddr = (struct dmfdevice *)reg; #ifdef lint br = 0; cvec = br; br = cvec; dmfxint(0); dmfrint(0); dmfsrint(); dmfsxint(); dmfdaint(); dmfdbint(); dmflint(); #endif br = 0x15; cvec = (uba_hd[numuba].uh_lastiv -= 4*8); dmfaddr->dmfccsr0 = cvec >> 2; /* NEED TO SAVE IT SOMEWHERE FOR OTHER DEVICES */ return (sizeof (struct dmfdevice)); } /* * Routine called to attach a dmf. */ dmfattach(ui) struct uba_device *ui; { dmfsoftCAR[ui->ui_unit] = ui->ui_flags; } /* * Open a DMF32 line, mapping the clist onto the uba if this * is the first dmf on this uba. Turn on this dmf if this is * the first use of it. */ /*ARGSUSED*/ dmfopen(dev, flag) dev_t dev; { register struct tty *tp; register int unit, dmf; register struct dmfdevice *addr; register struct uba_device *ui; int s; unit = minor(dev); dmf = unit >> 3; if (unit >= NDMF*8 || (ui = dmfinfo[dmf])== 0 || ui->ui_alive == 0) return (ENXIO); tp = &dmf_tty[unit]; if (tp->t_state&TS_XCLUDE && u.u_uid!=0) return (EBUSY); addr = (struct dmfdevice *)ui->ui_addr; tp->t_addr = (caddr_t)addr; tp->t_oproc = dmfstart; tp->t_state |= TS_WOPEN; /* * While setting up state for this uba and this dmf, * block uba resets which can clear the state. */ s = spl5(); #ifdef DMFDMA if (dmf_ubinfo[ui->ui_ubanum] == 0) { dmf_ubinfo[ui->ui_ubanum] = uballoc(ui->ui_ubanum, (caddr_t)cfree, nclist*sizeof(struct cblock), 0); cbase[ui->ui_ubanum] = dmf_ubinfo[ui->ui_ubanum]&0x3ffff; } #endif if ((dmfact&(1<<dmf)) == 0) { addr->dmfcsr |= DMF_IE; dmfact |= (1<<dmf); addr->dmfrsp = 1; /* DON'T KNOW WHAT TO SET IT TO YET */ } splx(s); /* * If this is first open, initialze tty state to default. */ if ((tp->t_state&TS_ISOPEN) == 0) { ttychars(tp); if (tp->t_ispeed == 0) { tp->t_ispeed = B300; tp->t_ospeed = B300; tp->t_flags = ODDP|EVENP|ECHO; } dmfparam(unit); } /* * Wait for carrier, then process line discipline specific open. */ if ((dmfmctl(dev, DMF_ON, DMSET) & (DMF_CAR<<8)) || (dmfsoftCAR[dmf] & (1<<(unit&07)))) tp->t_state |= TS_CARR_ON; s = spl5(); while ((tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; sleep((caddr_t)&tp->t_rawq, TTIPRI); } splx(s); return ((*linesw[tp->t_line].l_open)(dev, tp)); } /* * Close a DMF32 line. */ /*ARGSUSED*/ dmfclose(dev, flag) dev_t dev; int flag; { register struct tty *tp; register unit; unit = minor(dev); tp = &dmf_tty[unit]; (*linesw[tp->t_line].l_close)(tp); (void) dmfmctl(unit, DMF_BRK, DMBIC); if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) (void) dmfmctl(unit, DMF_OFF, DMSET); ttyclose(tp); } dmfread(dev, uio) dev_t dev; struct uio *uio; { register struct tty *tp; tp = &dmf_tty[minor(dev)]; return ((*linesw[tp->t_line].l_read)(tp, uio)); } dmfwrite(dev, uio) dev_t dev; struct uio *uio; { register struct tty *tp; tp = &dmf_tty[minor(dev)]; return ((*linesw[tp->t_line].l_write)(tp, uio)); } /* * DMF32 receiver interrupt. */ dmfrint(dmf) int dmf; { register struct tty *tp; register c; register struct dmfdevice *addr; register struct tty *tp0; register struct uba_device *ui; int overrun = 0; ui = dmfinfo[dmf]; if (ui == 0 || ui->ui_alive == 0) return; addr = (struct dmfdevice *)ui->ui_addr; tp0 = &dmf_tty[dmf<<3]; /* * Loop fetching characters from the silo for this * dmf until there are no more in the silo. */ while ((c = addr->dmfrbuf) < 0) { tp = tp0 + ((c>>8)&07); if (c & DMF_DSC) { addr->dmfcsr = DMF_IE | DMFIR_TBUF | ((c>>8)&07); if (addr->dmfrms & DMF_CAR) { if ((tp->t_state & TS_CARR_ON) == 0) { wakeup((caddr_t)&tp->t_rawq); tp->t_state |= TS_CARR_ON; } } else { if (tp->t_state & TS_CARR_ON) { gsignal(tp->t_pgrp, SIGHUP); gsignal(tp->t_pgrp, SIGCONT); addr->dmfcsr = DMF_IE | DMFIR_LCR | ((c>>8)&07); addr->dmfun.dmfirw &= 0xff; flushtty(tp, FREAD|FWRITE); } tp->t_state &= ~TS_CARR_ON; } continue; } if ((tp->t_state&TS_ISOPEN)==0) { wakeup((caddr_t)tp); continue; } if (c & DMF_PE) if ((tp->t_flags&(EVENP|ODDP))==EVENP || (tp->t_flags&(EVENP|ODDP))==ODDP ) continue; if ((c & DMF_DO) && overrun == 0) { printf("dmf%d: silo overflow\n", dmf); overrun = 1; } if (c & DMF_FE) /* * At framing error (break) generate * a null (in raw mode, for getty), or a * interrupt (in cooked/cbreak mode). */ if (tp->t_flags&RAW) c = 0; else c = tp->t_intrc; #if NBK > 0 if (tp->t_line == NETLDISC) { c &= 0177; BKINPUT(c, tp); } else #endif (*linesw[tp->t_line].l_rint)(c, tp); } } /* * Ioctl for DMF32. */ /*ARGSUSED*/ dmfioctl(dev, cmd, data, flag) dev_t dev; caddr_t data; { register struct tty *tp; register int unit = minor(dev); int error; tp = &dmf_tty[unit]; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) { if (cmd == TIOCSETP || cmd == TIOCSETN) dmfparam(unit); return (error); } switch (cmd) { case TIOCSBRK: (void) dmfmctl(dev, DMF_BRK, DMBIS); break; case TIOCCBRK: (void) dmfmctl(dev, DMF_BRK, DMBIC); break; case TIOCSDTR: (void) dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIS); break; case TIOCCDTR: (void) dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIC); break; case TIOCMSET: (void) dmfmctl(dev, dmtodmf(*(int *)data), DMSET); break; case TIOCMBIS: (void) dmfmctl(dev, dmtodmf(*(int *)data), DMBIS); break; case TIOCMBIC: (void) dmfmctl(dev, dmtodmf(*(int *)data), DMBIC); break; case TIOCMGET: *(int *)data = dmftodm(dmfmctl(dev, 0, DMGET)); break; default: return (ENOTTY); } return (0); } dmtodmf(bits) register int bits; { register int b; b = bits & 012; if (bits & DML_ST) b |= DMF_RATE; if (bits & DML_RTS) b |= DMF_RTS; if (bits & DML_USR) b |= DMF_USRW; return(b); } dmftodm(bits) register int bits; { register int b; b = (bits & 012) | ((bits >> 7) & 0760) | DML_LE; if (bits & DMF_USRR) b |= DML_USR; if (bits & DMF_RTS) b |= DML_RTS; return(b); } /* * Set parameters from open or stty into the DMF hardware * registers. */ dmfparam(unit) register int unit; { register struct tty *tp; register struct dmfdevice *addr; register int lpar, lcr; int s; tp = &dmf_tty[unit]; addr = (struct dmfdevice *)tp->t_addr; /* * Block interrupts so parameters will be set * before the line interrupts. */ s = spl5(); addr->dmfcsr = (unit&07) | DMFIR_LCR | DMF_IE; if ((tp->t_ispeed)==0) { tp->t_state |= TS_HUPCLS; (void) dmfmctl(unit, DMF_OFF, DMSET); return; } lpar = (dmf_speeds[tp->t_ospeed]<<12) | (dmf_speeds[tp->t_ispeed]<<8); lcr = DMFLCR_ENA; if ((tp->t_ispeed) == B134) lpar |= BITS6|PENABLE; else if (tp->t_flags & (RAW|LITOUT)) lpar |= BITS8; else { lpar |= BITS7|PENABLE; /* CHECK FOR XON/XOFF AND SET lcr |= DMF_AUTOX; */ } if ((tp->t_flags&EVENP) == 0) lpar |= OPAR; if ((tp->t_ospeed) == B110) lpar |= TWOSB; lpar |= (unit&07); addr->dmflpr = lpar; addr->dmfun.dmfirw = lcr | (addr->dmftms << 8); splx(s); } /* * DMF32 transmitter interrupt. * Restart the idle line. */ dmfxint(dmf) int dmf; { register struct tty *tp; register struct dmfdevice *addr; register struct uba_device *ui; register int unit, t; #ifdef DMFDMA short cntr; #endif ui = dmfinfo[dmf]; addr = (struct dmfdevice *)ui->ui_addr; while ((t = addr->dmfcsr) & DMF_TI) { unit = dmf*8 + ((t>>8)&07); tp = &dmf_tty[unit]; tp->t_state &= ~TS_BUSY; if (t & DMF_NXM) { printf("dmf%d: NXM line %d\n", dmf, unit&7); /* SHOULD RESTART OR SOMETHING... */ } if (tp->t_state&TS_FLUSH) tp->t_state &= ~TS_FLUSH; #ifdef DMFDMA else { addr->dmfcsr = DMFIR_TBUF | DMF_IE | (unit&07); if (addr->dmftsc == 0) { /* * Do arithmetic in a short to make up * for lost 16&17 bits. */ addr->dmfcsr = DMFIR_TBA | DMF_IE | (unit&07); cntr = addr->dmftba - UBACVT(tp->t_outq.c_cf, ui->ui_ubanum); ndflush(&tp->t_outq, (int)cntr); } } #endif if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else dmfstart(tp); } } /* * Start (restart) transmission on the given DMF32 line. */ dmfstart(tp) register struct tty *tp; { register struct dmfdevice *addr; register int car, dmf, unit, nch; int s; unit = minor(tp->t_dev); dmf = unit >> 3; unit &= 07; addr = (struct dmfdevice *)tp->t_addr; /* * Must hold interrupts in following code to prevent * state of the tp from changing. */ s = spl5(); /* * If it's currently active, or delaying, no need to do anything. */ if (tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) goto out; /* * If there are still characters in the silo, * just reenable the transmitter. */ addr->dmfcsr = DMF_IE | DMFIR_TBUF | unit; if (addr->dmftsc) { addr->dmfcsr = DMF_IE | DMFIR_LCR | unit; addr->dmfun.dmfirw |= DMF_TE; tp->t_state |= TS_BUSY; goto out; } /* * If there are sleepers, and output has drained below low * water mark, wake up the sleepers. */ if ((tp->t_state&TS_ASLEEP) && tp->t_outq.c_cc<=TTLOWAT(tp)) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } /* * Now restart transmission unless the output queue is * empty. */ if (tp->t_outq.c_cc == 0) goto out; if (tp->t_flags & (RAW|LITOUT)) nch = ndqb(&tp->t_outq, 0); else { nch = ndqb(&tp->t_outq, 0200); /* * If first thing on queue is a delay process it. */ if (nch == 0) { nch = getc(&tp->t_outq); timeout(ttrstrt, (caddr_t)tp, (nch&0x7f)+6); tp->t_state |= TS_TIMEOUT; goto out; } } /* * If characters to transmit, restart transmission. */ if (nch) { #ifdef DMFDMA addr->dmfcsr = DMF_IE | DMFIR_LCR | unit; addr->dmfun.dmfirw |= DMF_TE; car = UBACVT(tp->t_outq.c_cf, dmfinfo[dmf]->ui_ubanum); addr->dmfcsr = DMF_IE | DMFIR_TBA | unit; addr->dmftba = car; addr->dmftcc = ((car>>2)&0xc000) | nch; #else register char *cp = tp->t_outq.c_cf; register int i; nch = MIN(nch, DMF_SILOCNT); addr->dmfcsr = DMF_IE | DMFIR_LCR | unit; addr->dmfun.dmfirw |= DMF_TE; addr->dmfcsr = DMF_IE | DMFIR_TBUF | unit; for (i = 0; i < nch; i++) addr->dmftbuf = *cp++; ndflush(&tp->t_outq, nch); #endif tp->t_state |= TS_BUSY; } out: splx(s); } /* * Stop output on a line, e.g. for ^S/^Q or output flush. */ /*ARGSUSED*/ dmfstop(tp, flag) register struct tty *tp; { register struct dmfdevice *addr; register int unit, s; addr = (struct dmfdevice *)tp->t_addr; /* * Block input/output interrupts while messing with state. */ s = spl5(); if (tp->t_state & TS_BUSY) { /* * Device is transmitting; stop output * by selecting the line and disabling * the transmitter. If this is a flush * request then flush the output silo, * otherwise we will pick up where we * left off by enabling the transmitter. */ unit = minor(tp->t_dev); addr->dmfcsr = DMFIR_LCR | (unit&07) | DMF_IE; addr->dmfun.dmfirw &= ~DMF_TE; if ((tp->t_state&TS_TTSTOP)==0) { tp->t_state |= TS_FLUSH; addr->dmflcr |= DMF_FLUSH; } else tp->t_state &= ~TS_BUSY; } splx(s); } /* * DMF32 modem control */ dmfmctl(dev, bits, how) dev_t dev; int bits, how; { register struct dmfdevice *dmfaddr; register int unit, mbits, lcr; int s; unit = minor(dev); dmfaddr = (struct dmfdevice *)(dmf_tty[unit].t_addr); unit &= 07; s = spl5(); dmfaddr->dmfcsr = DMF_IE | DMFIR_TBUF | unit; mbits = dmfaddr->dmfrms << 8; dmfaddr->dmfcsr = DMF_IE | DMFIR_LCR | unit; mbits |= dmfaddr->dmftms; lcr = dmfaddr->dmflcr; switch (how) { case DMSET: mbits = bits; break; case DMBIS: mbits |= bits; break; case DMBIC: mbits &= ~bits; break; case DMGET: (void) splx(s); return(mbits); } if (mbits & DMF_BRK) lcr |= DMF_RBRK; else lcr &= ~DMF_RBRK; lcr &= ~DMF_MIE; /* for local use, this bit causes only problems*/ dmfaddr->dmfun.dmfirw = ((mbits & 037) << 8) + (lcr & 0377); (void) splx(s); return(mbits); } /* * Reset state of driver if UBA reset was necessary. * Reset the csr, lpr, and lcr registers on open lines, and * restart transmitters. */ dmfreset(uban) int uban; { register int dmf, unit; register struct tty *tp; register struct uba_device *ui; register struct dmfdevice *addr; int i; #ifdef DMFDMA if (dmf_ubinfo[uban] == 0) return; dmf_ubinfo[uban] = uballoc(uban, (caddr_t)cfree, nclist*sizeof (struct cblock), 0); cbase[uban] = dmf_ubinfo[uban]&0x3ffff; #endif for (dmf = 0; dmf < NDMF; dmf++) { ui = dmfinfo[dmf]; if (ui == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban) continue; printf(" dmf%d", dmf); addr = (struct dmfdevice *)ui->ui_addr; addr->dmfcsr = DMF_IE; addr->dmfrsp = 1; unit = dmf * 8; for (i = 0; i < 8; i++) { tp = &dmf_tty[unit]; if (tp->t_state & (TS_ISOPEN|TS_WOPEN)) { dmfparam(unit); (void) dmfmctl(unit, DMF_ON, DMSET); tp->t_state &= ~TS_BUSY; dmfstart(tp); } unit++; } } } /* stubs for interrupt routines for devices not yet supported */ dmfsrint() { printf("dmfsrint\n"); } dmfsxint() { printf("dmfsxint\n"); } dmfdaint() { printf("dmfdaint\n"); } dmfdbint() { printf("dmfdbint\n"); } dmflint() { printf("dmflint\n"); } #endif