/* * Copyright (c) 1985, 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)dmz.c 7.1 (Berkeley) 6/5/86 */ /* * DMZ-32 driver * HISTORY * 23-Apr-85 Joe Camaratta (jcc) at Siemens RTL * Driver for DEC's DMZ32 24-line asynchronous multiplexor. * Based on Chris Maloney's driver for DEC's DMF32 * * 9-Aug-85 Mike Meyer (mwm) at ucb * Mangled into shape for 4.3. */ #include "dmz.h" #if NDMZ > 0 #include "../machine/pte.h" #include "bk.h" #include "uba.h" #include "param.h" #include "conf.h" #include "dir.h" #include "user.h" #include "proc.h" #include "ioctl.h" #include "tty.h" #include "map.h" #include "buf.h" #include "vm.h" #include "bkmac.h" #include "clist.h" #include "file.h" #include "uio.h" #include "kernel.h" #include "syslog.h" #include "ubareg.h" #include "ubavar.h" #include "dmzreg.h" #include "dmreg.h" int dmzprobe(), dmzattach(), dmzrint(), dmzxint(); struct uba_device *dmzinfo[NDMZ]; u_short dmzstd[] = {0, 0}; struct uba_driver dmzdriver = { dmzprobe, 0, dmzattach, 0, dmzstd, "dmz", dmzinfo }; #define NDMZLINES (NDMZ*24) int ttrstrt(); struct tty dmz_tty[NDMZLINES]; int dmzsoftCAR[NDMZ]; struct { char dmz_state; /* dmz state */ int dmz_count; /* dmz dma count */ } dmz_softc[NDMZ*24]; #define ST_TXOFF (0x01) /* transmission turned off (^S) */ #define ST_DMA (0x02) /* dma inprogress */ #define ST_INBUSY (0x04) /* stop transmission in busy */ char dmz_speeds[] = { 0, 0, 1, 2, 3, 4, 0, 5, 6, 7, 010, 012, 014, 016, 017, 0 }; #ifndef PORTSELECTOR #define ISPEED B9600 #define IFLAGS (EVENP|ODDP|ECHO) #else #define ISPEED B4800 #define IFLAGS (EVENP|ODDP) #endif #ifndef lint int ndmz = NDMZLINES; /* Used by pstat/iostat */ #endif short dmzact[NDMZ]; /* Mask of active octets on the dmz */ int dmzstart(); /* * SILO_TIMEOUT represents the number of milliseconds characters can sit * in the input silo without causing an interrupt. If data overruns or * slow XON/XOFF occur, set it lower but AT LEAST equal to 1. */ #define SILO_TIMEOUT (3) /* * DO_DMA_COUNT represents the threshold of the number of output * characters beyond which the driver uses DMA mode. */ #define DO_DMA_COUNT (10) #define TRUE (1) #define FALSE (0) int cbase[NUBA]; /* base address in unibus map */ int dmz_ubinfo[NUBA]; /* info about allocated unibus map */ #define UBACVT(x, uban) (cbase[uban] + ((x) - (char *)cfree)) /* These flags are for debugging purposes only */ int dmz_dma_on = 1; dmzprobe(reg) caddr_t reg; { register int br, cvec; register struct dmzdevice *dmz_addr; register unsigned int a; dmz_addr = (struct dmzdevice *)reg; #ifdef lint br = 0; cvec = br; br = cvec; dmzxinta(0); dmzxintb(0); dmzxintc(0); dmzrinta(0); dmzrintb(0); dmzrintc(0); #endif br = 0x15; a = dmz_addr->dmz_config; if (((a>>12) & ~DMZ_INTERFACE) != 0) { printf(" Unknown interface type\n"); return (0); } if (((a>>8) & DMZ_NOC_MASK) != 3) { printf(" Not all octets are available\n"); return (0); } cvec = (uba_hd[numuba].uh_lastiv -= 4 * 6); dmz_addr->dmz_config = cvec >> 2; return (sizeof(struct dmzdevice)); } dmzattach(ui) struct uba_device *ui; { dmzsoftCAR[ui->ui_unit] = ui->ui_flags; cbase[ui->ui_ubanum] = -1; } /* ARGSUSED */ dmzopen(device, flag) dev_t device; int flag; { register struct tty *tp; register int unit, controller; register struct dmzdevice *dmz_addr; register struct uba_device *ui; int priority; int octet; unit = minor(device); controller = DMZ(unit); octet = OCTET(unit); if (unit >= NDMZLINES || (ui = dmzinfo[controller]) == 0 || ui->ui_alive == 0) return (ENXIO); tp = &dmz_tty[unit]; if ((tp->t_state & TS_XCLUDE) && u.u_uid != 0) return (EBUSY); dmz_addr = (struct dmzdevice *)ui->ui_addr; tp->t_addr = (caddr_t)dmz_addr; tp->t_oproc = dmzstart; /* * Set up Unibus map registers. Block uba resets, which can * clear the state. */ priority = spl5(); if (cbase[ui->ui_ubanum] == -1) { dmz_ubinfo[ui->ui_ubanum] = uballoc(ui->ui_ubanum, (caddr_t)cfree, nclist * sizeof(struct cblock), 0); if (dmz_ubinfo[ui->ui_ubanum] == 0) { splx(priority); printf("dmz: insufficient unibus map regs\n"); return (ENOMEM); } cbase[ui->ui_ubanum] = UBAI_ADDR(dmz_ubinfo[ui->ui_ubanum]); } if ((dmzact[controller] & (1 << octet)) == 0) { dmz_addr->octet[octet].octet_csr |= DMZ_IE; dmzact[controller] |= 1 << octet; dmz_addr->octet[octet].octet_receive.octet_sato = SILO_TIMEOUT; } splx(priority); if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); #ifndef PORTSELECTOR if (tp->t_ispeed == 0) { #else tp->t_state |= TS_HUPCLS; #endif PORTSELECTOR tp->t_ispeed = ISPEED; tp->t_ospeed = ISPEED; tp->t_flags = IFLAGS; #ifndef PORTSELECTOR } #endif PORTSELECTOR dmz_softc[unit].dmz_state = 0; } dmzparam(unit); /* * Wait for carrier, then process line discipline specific open. */ if ((dmzmctl(unit, DMZ_ON, DMSET) & DMZ_CAR) || (dmzsoftCAR[controller] & (1 << (unit % 24)))) tp->t_state |= TS_CARR_ON; priority = spl5(); while ((tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; sleep((caddr_t) &tp->t_rawq, TTIPRI); } splx(priority); return ((*linesw[tp->t_line].l_open)(device, tp)); } dmzparam(unit) register int unit; { register struct tty *tp; register struct dmzdevice *dmz_addr; register int line_parameters; register int octet; int priority; octet = OCTET(unit); tp = &dmz_tty[unit]; dmz_addr = (struct dmzdevice *)tp->t_addr; priority = spl5(); if ((tp->t_ispeed) == 0) { tp->t_state |= TS_HUPCLS; (void) dmzmctl(unit, DMZ_OFF, DMSET); splx(priority); return; } line_parameters = (dmz_speeds[tp->t_ospeed] << 12) | (dmz_speeds[tp->t_ispeed] << 8); if ((tp->t_ispeed) == B134) line_parameters |= DMZ_6BT | DMZ_PEN; else if (tp->t_flags & (RAW | LITOUT | PASS8)) line_parameters |= DMZ_8BT; else line_parameters |= DMZ_7BT | DMZ_PEN; if (tp->t_flags & EVENP) line_parameters |= DMZ_EPR; if ((tp->t_ospeed) == B110) line_parameters |= DMZ_SCD; line_parameters |= (unit & 07); dmz_addr->octet[octet].octet_lprm = line_parameters; splx(priority); } /* ARGSUSED */ dmzclose(device, flag) dev_t device; int flag; { register struct tty *tp; register int unit; unit = minor(device); tp = &dmz_tty[unit]; (*linesw[tp->t_line].l_close)(tp); /* * Clear break, hang-up and close the modem. */ (void) dmzmctl(unit, DMZ_BRK, DMBIC); if (tp->t_state & TS_HUPCLS || (tp->t_state & TS_ISOPEN) == 0) (void) dmzmctl(unit, DMZ_OFF, DMSET); ttyclose(tp); return; } dmzreset(uban) int uban; { register int controller, unit; register struct tty *tp; register struct uba_device *ui; register struct dmzdevice *dmz_addr; int i; int octet; for (controller = 0; controller < NDMZ; controller++) { ui = dmzinfo[controller]; if (ui == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban) continue; printf("dmz%d ", controller); dmz_addr = (struct dmzdevice *) ui->ui_addr; if (dmz_ubinfo[uban]) { dmz_ubinfo[uban] = uballoc(uban, (caddr_t)cfree, nclist * sizeof(struct cblock), 0); cbase[uban] = UBAI_ADDR(dmz_ubinfo[uban]); } for (octet = 0; octet < 3; octet++) if ((dmzact[controller] & (1 << octet)) != 0) { dmz_addr->octet[octet].octet_csr |= DMZ_IE; dmz_addr->octet[octet].octet_receive.octet_sato = SILO_TIMEOUT; } unit = controller * 24; /* * If a unit is open or waiting for open to complete, * reset it. */ for (i = 0; i < 24; i++) { dmz_softc[unit].dmz_state = 0; tp = &dmz_tty[unit]; if (tp->t_state & (TS_ISOPEN | TS_WOPEN)) { dmzparam(unit); (void) dmzmctl(unit, DMZ_ON, DMSET); tp->t_state &= ~TS_BUSY; dmzstart(tp); } unit++; } } return; } dmzread(device, uio) dev_t device; struct uio *uio; { register struct tty *tp; int xstatus; tp = &dmz_tty[minor(device)]; xstatus = (*linesw[tp->t_line].l_read)(tp, uio); return (xstatus); } dmzwrite(device, uio) dev_t device; struct uio *uio; { register struct tty *tp; int xstatus; tp = &dmz_tty[minor(device)]; xstatus = (*linesw[tp->t_line].l_write)(tp, uio); return (xstatus); } dmzrinta(controller) int controller; { dmzrint(controller, 0); } dmzrintb(controller) int controller; { dmzrint(controller, 1); } dmzrintc(controller) int controller; { dmzrint(controller, 2); } dmzrint(controller, octet) int controller; register int octet; { register struct tty *tp; register int character; register struct dmzdevice *dmz_addr; register struct tty *tp0; register int unit; register struct uba_device *ui; int overrun; overrun = 0; ui = dmzinfo[controller]; if (ui == 0 || ui->ui_alive == 0) return; dmz_addr = (struct dmzdevice *) ui->ui_addr; tp0 = &dmz_tty[controller * 24]; while ((character = dmz_addr->octet[octet].octet_receive.octet_rb) < 0) { unit = (character >> 8) & 07; /* unit is bits 8-10 of rb */ tp = tp0 + (octet * 8 + unit); if (character & DMZ_DSC) { dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_RMSTSC | unit; if (dmz_addr->octet[octet].octet_rmstsc & DMZ_CAR) (void)(*linesw[tp->t_line].l_modem)(tp, 1); else if ((dmzsoftCAR[controller] & (1 << (octet * 8 + unit))) == 0 && (*linesw[tp->t_line].l_modem)(tp, 0) == 0) (void)dmzmctl(tp - dmz_tty, DMZ_OFF, DMSET); continue; } if ((tp->t_state&TS_ISOPEN)==0) { wakeup((caddr_t)&tp->t_rawq); #ifdef PORTSELECTOR if ((tp->t_state&TS_WOPEN) == 0) #endif continue; } if (character & DMZ_PE) { if ((tp->t_flags & (EVENP | ODDP)) == EVENP || (tp->t_flags & (EVENP | ODDP)) == ODDP) continue; } if ((character & DMZ_DO) && overrun == 0) { log(LOG_WARNING, "dmz%d: silo overflow\n", controller); overrun = 1; } if (character & DMZ_FE) { if (tp->t_flags & RAW) character = 0; else character = tp->t_intrc; } (*linesw[tp->t_line].l_rint)(character, tp); } return; } dmzxinta(controller) int controller; { dmzxint(controller, 0); } dmzxintb(controller) int controller; { dmzxint(controller, 1); } dmzxintc(controller) int controller; { dmzxint(controller, 2); } dmzxint(controller, octet) int controller; register int octet; { register struct tty *tp; register struct dmzdevice *dmz_addr; register struct uba_device *ui; register int unit, t; int priority; ui = dmzinfo[controller]; dmz_addr = (struct dmzdevice *)ui->ui_addr; priority = spl5(); while ((t = dmz_addr->octet[octet].octet_csr) & DMZ_TRDY) { unit = controller * 24 + (octet * 8 + ((t>>8) & 07)); tp = &dmz_tty[unit]; tp->t_state &= ~TS_BUSY; if (t & DMZ_NXM) printf("dmz%d: NXM line %d\n", controller, octet * 8 + (unit & 07)); if (tp->t_state & TS_FLUSH) { tp->t_state &= ~TS_FLUSH; dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_LCTMR | (unit & 07); dmz_addr->octet[octet].octet_lctmr = (dmz_addr->octet[octet].octet_lctmr | DMZ_TE); } else if (dmz_softc[unit].dmz_state & ST_DMA) ndflush(&tp->t_outq, dmz_softc[unit].dmz_count); dmz_softc[unit].dmz_state = 0; if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else dmzstart(tp); } splx(priority); return; } dmzstart(tp) register struct tty *tp; { register struct dmzdevice *dmz_addr; register int unit, nch, room; int controller, octet; int priority, car, use_dma; register int i; register char *cp; unit = minor(tp->t_dev); controller = DMZ(unit); octet = OCTET(unit); dmz_addr = (struct dmzdevice *)tp->t_addr; priority = spl5(); if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) goto out; /* * If the transmitter has been disabled, reenable it. * If the transmitter was disabled before the xint (the * ST_INBUSY was still on), then reset the BUSY state and * we will wait for the interrupt. If !TS_BUSY, we already * saw the interrupt so we can start another transmission. */ if (dmz_softc[unit].dmz_state & ST_TXOFF) { dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_LCTMR | (unit & 07); dmz_addr->octet[octet].octet_lctmr = (dmz_addr->octet[octet].octet_lctmr | DMZ_TE); dmz_softc[unit].dmz_state &= ~ST_TXOFF; if (dmz_softc[unit].dmz_state & ST_INBUSY) { dmz_softc[unit].dmz_state &= ~ST_INBUSY; tp->t_state |= TS_BUSY; goto out; } } if (tp->t_outq.c_cc <= TTLOWAT(tp)) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } if (tp->t_wsel) { selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); tp->t_wsel = 0; tp->t_state &= ~TS_WCOLL; } } 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 (nch == 0) { nch = getc(&tp->t_outq); timeout(ttrstrt, (caddr_t)tp, (nch & 0x7f)+6); tp->t_state |= TS_TIMEOUT; goto out; } } /* * Should we use DMA or SILO mode? * If nch is greater than DO_DMA_COUNT then DMA. */ if (nch) { dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_LCTMR | (unit & 07); dmz_addr->octet[octet].octet_lctmr = (dmz_addr->octet[octet].octet_lctmr | DMZ_TE); tp->t_state |= TS_BUSY; use_dma = FALSE; room = DMZ_SIZ; if (nch > DO_DMA_COUNT) use_dma = TRUE; if (use_dma && dmz_dma_on) { car = UBACVT(tp->t_outq.c_cf, dmzinfo[controller]->ui_ubanum); dmz_softc[unit].dmz_count = nch; dmz_softc[unit].dmz_state |= ST_DMA; dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_TBA | (unit & 07); dmz_addr->octet[octet].octet_tba = car; dmz_addr->octet[octet].octet_tcc = ((car >> 2) & 0xc000) | nch; } else { dmz_softc[unit].dmz_state &= ~ST_DMA; cp = tp->t_outq.c_cf; nch = MIN(nch, room); dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_TBUF | (unit & 07); for (i = 0; i < nch; i++) dmz_addr->octet[octet].octet_tbf = *cp++ ; ndflush(&tp->t_outq, nch); } } out: splx(priority); return; } /* ARGSUSED */ dmzstop(tp, flag) register struct tty *tp; { register struct dmzdevice *dmz_addr; register int unit, priority, octet; priority = spl5(); dmz_addr = (struct dmzdevice *) tp->t_addr; unit = minor(tp->t_dev); octet = OCTET(unit); dmz_addr->octet[octet].octet_csr = IR_LCTMR | (unit & 07) | DMZ_IE; dmz_addr->octet[octet].octet_lctmr = (dmz_addr->octet[octet].octet_lctmr & ~DMZ_TE); dmz_softc[unit].dmz_state |= ST_TXOFF; if ((tp->t_state & TS_TTSTOP) == 0) { tp->t_state |= (TS_FLUSH | TS_BUSY); dmz_addr->octet[octet].octet_lctmr = (dmz_addr->octet[octet].octet_lctmr | DMZ_FLS); } else if (tp->t_state & TS_BUSY) { dmz_softc[unit].dmz_state |= ST_INBUSY; tp->t_state &= ~TS_BUSY; } splx(priority); return; } /* ARGSUSED */ dmzioctl(device, command, data, flag) dev_t device; caddr_t data; { register struct tty *tp; register int unit; int error; unit = minor(device); tp = &dmz_tty[unit]; error = (*linesw[tp->t_line].l_ioctl)(tp, command, data, flag); if (error >= 0) return (error); error = ttioctl(tp, command, data, flag); if (error >= 0) { if (command == TIOCSETP || command == TIOCSETN || command == TIOCLSET || command == TIOCLBIS || command == TIOCLBIC) dmzparam(unit); return (error); } switch (command) { case TIOCSBRK: (void) dmzmctl(unit, DMZ_BRK, DMBIS); break; case TIOCCBRK: (void) dmzmctl(unit, DMZ_BRK, DMBIC); break; case TIOCSDTR: (void) dmzmctl(unit, DMZ_DTR | DMZ_RTS, DMBIS); break; case TIOCCDTR: (void) dmzmctl(unit, DMZ_DTR | DMZ_RTS, DMBIC); break; case TIOCMSET: (void) dmzmctl(unit, dmtodmz(*(int *)data), DMSET); break; case TIOCMBIS: (void) dmzmctl(unit, dmtodmz(*(int *)data), DMBIS); break; case TIOCMBIC: (void) dmzmctl(unit, dmtodmz(*(int *)data), DMBIC); break; case TIOCMGET: *(int *)data = dmzmctl(unit, 0, DMGET); break; default: return (ENOTTY); } return (0); } dmzmctl(unit, bits, how) register int unit; int bits, how; { register struct dmzdevice *dmz_addr; register int modem_status, line_control; int priority; int octet; octet = OCTET(unit); dmz_addr = (struct dmzdevice *) dmzinfo[DMZ(unit)]->ui_addr; priority = spl5(); dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_RMSTSC | (unit & 07); modem_status = dmz_addr->octet[octet].octet_rmstsc & 0xff00; dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_LCTMR | (unit & 07); line_control = dmz_addr->octet[octet].octet_lctmr; switch (how) { case DMSET: line_control = bits; break; case DMBIS: line_control |= bits; break; case DMBIC: line_control &= ~bits; break; case DMGET: (void) splx(priority); return (dmztodm(modem_status, line_control)); } dmz_addr->octet[octet].octet_csr = DMZ_IE | IR_LCTMR | (unit & 07); dmz_addr->octet[octet].octet_lctmr = line_control; splx(priority); return (modem_status); } /* * Routine to convert modem status from dm to dmz lctmr format. */ dmtodmz(bits) register int bits; { register int lcr = DMZ_LCE; if (bits & DML_DTR) lcr |= DMZ_DTR; if (bits & DML_RTS) lcr |= DMZ_RTS; if (bits & DML_ST) lcr |= DMF_ST; if (bits & DML_USR) lcr |= DMZ_USRW; return (lcr); } /* * Routine to convert modem status from dmz receive modem status * and line control register to dm format. * If dmz user modem read bit set, set DML_USR. */ dmztodm(rms, lcr) register int rms, lcr; { rms = ((rms & (DMZ_DSR|DMZ_RNG|DMZ_CAR|DMZ_CTS|DMF_SR)) >> 7) | ((rms & DMZ_USRR) >> 1) | DML_LE; if (lcr & DMZ_DTR) rms |= DML_DTR; if (lcr & DMF_ST) rms |= DML_ST; if (lcr & DMZ_RTS) rms |= DML_RTS; return (rms); } #endif