perry@sbcs.UUCP (Perry Kivolowitz) (09/21/83)
many people who have the original version of this driver can't use 0 and 1 for local (hardwired) use - this is because the modem control routines just didn't make any sense. You MUST jumper dsr to dtr on 0 and 1 ------------------------------------------------------------------- /* dmf.c 4.3 82/05/27 */ #include "dmf.h" #if NDMF > 0 #define DMFDMA /* * 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 RJ Spanbauer, SUNY/Stony Brook to operate correctly * on "modem" ports (ttybase+ [0,1]) without a full complement of RS232 * handshake lines. */ #include "bk.h" #include "../h/param.h" #include "../h/conf.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/tty.h" #include "../h/map.h" #include "../h/pte.h" #include "../h/buf.h" #include "../h/vm.h" #include "../h/ubareg.h" #include "../h/ubavar.h" #include "../h/bk.h" #include "../h/clist.h" #include "../h/file.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]; int ndmf = NDMF*8; 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; #endif br = 0x15; cvec = (uba_hd[numuba].uh_lastiv -= 4*8); dmfaddr->dmfccsr0 = cvec >> 2; /* NEED TO SAVE IT SOMEWHERE FOR OTHER DEVICES */ return (1); } /* * Routine called to attach a dmf. */ dmfattach(ui) struct uba_device *ui; { printf("dmf flags: %x\n" , ui->ui_flags); 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) { u.u_error = ENXIO; return; } #ifdef DEBUG printf("dmf%d,%d opening\n", dmf, unit); #endif tp = &dmf_tty[unit]; if (tp->t_state&XCLUDE && u.u_uid!=0) { u.u_error = EBUSY; return; } addr = (struct dmfdevice *)ui->ui_addr; tp->t_addr = (caddr_t)addr; tp->t_oproc = dmfstart; tp->t_state |= 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&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 |= CARR_ON; s = spl5(); while ((tp->t_state & CARR_ON) == 0) { tp->t_state |= WOPEN; sleep((caddr_t)&tp->t_rawq, TTIPRI); } splx(s); (*linesw[tp->t_line].l_open)(dev, tp); #ifdef DEBUG printf("exit open\n"); #endif } /* * 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]; #ifdef DEBUG printf("dmf%d,%d close\n", unit>>3, unit); #endif (*linesw[tp->t_line].l_close)(tp); dmfmctl(unit, DMF_BRK, DMBIC); if (tp->t_state&HUPCLS || (tp->t_state&ISOPEN)==0) dmfmctl(unit, DMF_OFF, DMSET); ttyclose(tp); } dmfread(dev) dev_t dev; { register struct tty *tp; tp = &dmf_tty[minor(dev)]; (*linesw[tp->t_line].l_read)(tp); } dmfwrite(dev) dev_t dev; { register struct tty *tp; tp = &dmf_tty[minor(dev)]; (*linesw[tp->t_line].l_write)(tp); } /* * 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 & CARR_ON) == 0) { wakeup((caddr_t)&tp->t_rawq); tp->t_state |= CARR_ON; } } else { if (tp->t_state & 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 &= ~CARR_ON; } #ifdef DEBUG printf("data set change\n"); #endif continue; } if ((tp->t_state&ISOPEN)==0) { wakeup((caddr_t)tp); #ifdef DEBUG printf("wasn't open\n"); #endif continue; } if (c & DMF_PE) if ((tp->t_flags&(EVENP|ODDP))==EVENP || (tp->t_flags&(EVENP|ODDP))==ODDP ) { #ifdef DEBUG printf("parity error\n"); #endif 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 = tun.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, addr, flag) dev_t dev; caddr_t addr; { register struct tty *tp; register int unit = minor(dev); register int dmf = unit >> 3; register struct device *dmfaddr; int temp; tp = &dmf_tty[unit]; cmd = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr); if (cmd == 0) return; if (ttioctl(tp, cmd, addr, flag)) { if (cmd==TIOCSETP || cmd==TIOCSETN) dmfparam(unit); } else switch(cmd) { case TIOCSBRK: dmfmctl(dev, DMF_BRK, DMBIS); break; case TIOCCBRK: dmfmctl(dev, DMF_BRK, DMBIC); break; case TIOCSDTR: dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIS); break; case TIOCCDTR: dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIC); break; case TIOCMSET: if (copyin(addr, (caddr_t) &temp, sizeof(temp))) u.u_error = EFAULT; else dmfmctl(dev, dmtodmf(temp), DMSET); break; case TIOCMBIS: if (copyin(addr, (caddr_t) &temp, sizeof(temp))) u.u_error = EFAULT; else dmfmctl(dev, dmtodmf(temp), DMBIS); break; case TIOCMBIC: if (copyin(addr, (caddr_t) &temp, sizeof(temp))) u.u_error = EFAULT; else dmfmctl(dev, dmtodmf(temp), DMBIC); break; case TIOCMGET: temp = dmftodm(dmfmctl(dev, 0, DMGET)); if (copyout((caddr_t) &temp, addr, sizeof(temp))) u.u_error = EFAULT; break; default: u.u_error = ENOTTY; } } 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 |= HUPCLS; 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) || (tp->t_local&LLITOUT)) 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 &= ~BUSY; if (t & DMF_NXM) { printf("dmf%d: NXM line %d\n", dmf, unit&7); /* SHOULD RESTART OR SOMETHING... */ } if (tp->t_state&FLUSH) tp->t_state &= ~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&(TIMEOUT|BUSY|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 |= BUSY; goto out; } /* * If there are sleepers, and output has drained below low * water mark, wake up the sleepers. */ if ((tp->t_state&ASLEEP) && tp->t_outq.c_cc<=TTLOWAT(tp)) { tp->t_state &= ~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 || tp->t_local&LLITOUT) 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 |= 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 |= 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 & 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&TTSTOP)==0) { tp->t_state |= FLUSH; addr->dmflcr |= DMF_FLUSH; } else tp->t_state &= ~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; ubarelse(uban, &dmf_ubinfo[uban]); 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 & (ISOPEN|WOPEN)) { dmfparam(unit); dmfmctl(unit, DMF_ON, DMSET); tp->t_state &= ~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