+ if (vs->vs_vers == VXV_NEW)
+ return (vs->vs_vers == VXV_NEW ? 1 : (cp->cmd&07) == 7);
+}
+
+/*
+ * Write a command out to the VIOC
+ */
+vcmd(vx, cmdad)
+ register int vx;
+ register caddr_t cmdad;
+{
+ register struct vcmds *cp;
+ register struct vx_softc *vs;
+ int s;
+
+ s = spl8();
+ vs = &vx_softc[vx];
+ if (vs->vs_state == VXS_RESET && cmdad != NULL) {
+ /*
+ * When the vioc is resetting, don't process
+ * anything other than LIDENT commands.
+ */
+ register struct vxcmd *cmdp = (struct vxcmd *)
+ ((char *)cmdad - sizeof (cmdp->c_fwd));
+
+ if (cmdp->cmd != VXC_LIDENT) {
+ vrelease(vs, cmdp);
+ return (0);
+ }
+ }
+ cp = &vs->vs_cmds;
+ if (cmdad != (caddr_t)0) {
+ cp->cmdbuf[cp->v_fill] = cmdad;
+ if (++cp->v_fill >= VC_CMDBUFL)
+ cp->v_fill = 0;
+ if (cp->v_fill == cp->v_empty) {
+ printf("vx%d: cmd q overflow\n", vx);
+ vxstreset(vx);
+ splx(s);
+ return (0);
+ }
+ cp->v_cmdsem++;
+ }
+ if (cp->v_cmdsem && cp->v_curcnt < vs->vs_maxcmd) {
+ cp->v_cmdsem--;
+ cp->v_curcnt++;
+ vinthandl(vx, ((V_BSY|CMDquals) << 8)|V_INTR);
+ }
+ splx(s);
+ return (1);
+}
+
+/*
+ * VIOC acknowledge interrupt. The VIOC has received the new
+ * command. If no errors, the new command becomes one of 16 (max)
+ * current commands being executed.
+ */
+vackint(vx)
+ register vx;
+{
+ register struct vxdevice *vp;
+ register struct vcmds *cp;
+ struct vx_softc *vs;
+ int s;
+
+ scope_out(5);
+ vs = &vx_softc[vx];
+ if (vs->vs_type) { /* Its a BOP */
+#ifdef SNA_DEBUG
+ extern vbrall();
+
+ if (snadebug & SVIOC)
+ printf("vx%d: vack interrupt from BOP\n", vx);
+ vbrall(vx); /* Int. from BOP, port 0 */
+#endif
+ return;
+ }
+ s = spl8();
+ vp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ cp = &vs->vs_cmds;
+ if (vp->v_vcid & V_ERR) {
+ register char *resp;
+ register i;
+ printf("vx%d INTR ERR type %x v_dcd %x\n", vx,
+ vp->v_vcid & 07, vp->v_dcd & 0xff);
+ /* resp = (char *)vp + (vp->v_rspoff & 0x7FFF); */
+ resp = (char *)vs->vs_mricmd;
+ for (i = 0; i < 16; i++)
+ printf("%x ", resp[i]&0xff);
+ printf("\n");
+ splx(s);
+ vxstreset(vx);
+ return;
+ }
+ if ((vp->v_hdwre&017) == CMDquals) {
+#ifdef VX_DEBUG
+ if (vxintr4 & VXERR4) { /* causes VIOC INTR ERR 4 */
+ register struct vxcmd *cp1;
+ register struct vxcmd *cp0 = (struct vxcmd *)
+ ((long)cp->cmdbuf[cp->v_empty] - 4);
+
+ if (cp0->cmd == VXC_XMITDTA || cp0->cmd == VXC_XMITIMM) {
+ cp1 = vobtain(vs);
+ *cp1 = *cp0;
+ vxintr4 &= ~VXERR4;
+ (void) vcmd(vx, &cp1->cmd);
+ }
+ }
+#endif
+ cp->v_curcmd[vp->v_vcid & VCMDLEN-1] = cp->cmdbuf[cp->v_empty];
+ if (++cp->v_empty >= VC_CMDBUFL)
+ cp->v_empty = 0;
+ }
+ if (++cp->v_itrempt >= VC_IQLEN)
+ cp->v_itrempt = 0;
+ vintempt(vx);
+ splx(s);
+ (void) vcmd(vx, (caddr_t)0); /* queue next cmd, if any */
+}
+
+/*
+ * Command Response interrupt. The Vioc has completed
+ * a command. The command may now be returned to
+ * the appropriate device driver.
+ */
+vcmdrsp(vx)
+ register vx;
+{
+ register struct vxdevice *vp;
+ register struct vcmds *cp;
+ register caddr_t cmd;
+ register struct vx_softc *vs;
+ register char *resp;
+ register k;
+ register int s;
+
+ scope_out(6);
+ vs = &vx_softc[vx];
+ if (vs->vs_type) { /* Its a BOP */
+ printf("vx%d: vcmdrsp interrupt\n", vx);
+ return;
+ }
+ s = spl8();
+ vp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ cp = &vs->vs_cmds;
+ resp = (char *)vp + (vp->v_rspoff&0x7fff);
+ if (((k = resp[1])&V_UNBSY) == 0) {
+ printf("vx%d: cmdresp debug\n", vx);
+ splx(s);
+ vxstreset(vx);
+ return;
+ }
+ k &= VCMDLEN-1;
+ cmd = cp->v_curcmd[k];
+ cp->v_curcmd[k] = (caddr_t)0;
+ cp->v_curcnt--;
+ k = *((short *)&resp[4]); /* cmd operation code */
+ if ((k&0xff00) == VXC_LIDENT) /* want hiport number */
+ for (k = 0; k < VRESPLEN; k++)
+ cmd[k] = resp[k+4];
+ resp[1] = 0;
+ vxxint(vx, (struct vxcmd *)cmd);
+ if (vs->vs_state == VXS_READY)
+ vinthandl(vx, ((V_BSY|RSPquals) << 8)|V_INTR);
+ splx(s);
+}
+
+/*
+ * Unsolicited interrupt.
+ */
+vunsol(vx)
+ register vx;
+{
+ register struct vxdevice *vp;
+ struct vx_softc *vs;
+ int s;
+
+ scope_out(1);
+ vs = &vx_softc[vx];
+ if (vs->vs_type) { /* Its a BOP */
+ printf("vx%d: vunsol from BOP\n", vx);
+ return;
+ }
+ s = spl8();
+ vp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ if (vp->v_uqual&V_UNBSY) {
+ vxrint(vx);
+ vinthandl(vx, ((V_BSY|UNSquals) << 8)|V_INTR);
+#ifdef notdef
+ } else {
+ printf("vx%d: unsolicited interrupt error\n", vx);
+ splx(s);
+ vxstreset(vx);
+#endif
+ }
+ splx(s);
+}
+
+/*
+ * Enqueue an interrupt
+ */
+vinthandl(vx, item)
+ register int vx;
+ register item;
+{
+ register struct vcmds *cp;
+ int empty;
+
+ cp = &vx_softc[vx].vs_cmds;
+ empty = cp->v_itrfill == cp->v_itrempt;
+ cp->v_itrqueu[cp->v_itrfill] = item;
+ if (++cp->v_itrfill >= VC_IQLEN)
+ cp->v_itrfill = 0;
+ if (cp->v_itrfill == cp->v_itrempt) {
+ printf("vx%d: interrupt q overflow\n", vx);
+ vxstreset(vx);
+ } else if (empty)
+ vintempt(vx);
+}
+
+vintempt(vx)
+ register int vx;
+{
+ register struct vcmds *cp;
+ register struct vxdevice *vp;
+ register short item;
+ register short *intr;
+
+ vp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ if (vp->v_vioc&V_BSY)
+ return;
+ cp = &vx_softc[vx].vs_cmds;
+ if (cp->v_itrempt == cp->v_itrfill)
+ return;
+ item = cp->v_itrqueu[cp->v_itrempt];
+ intr = (short *)&vp->v_vioc;
+ switch ((item >> 8)&03) {
+
+ case CMDquals: { /* command */
+ int phys;
+
+ if (cp->v_empty == cp->v_fill || vp->v_vcbsy&V_BSY)
+ break;
+ vx_softc[vx].vs_mricmd = (caddr_t)cp->cmdbuf[cp->v_empty];
+ phys = vtoph((struct proc *)0,
+ (unsigned)cp->cmdbuf[cp->v_empty]);
+ vp->v_vcp[0] = ((short *)&phys)[0];
+ vp->v_vcp[1] = ((short *)&phys)[1];
+ vp->v_vcbsy = V_BSY;
+ *intr = item;
+ scope_out(4);
+ break;
+ }
+
+ case RSPquals: /* command response */
+ *intr = item;
+ scope_out(7);
+ break;
+
+ case UNSquals: /* unsolicited interrupt */
+ vp->v_uqual = 0;
+ *intr = item;
+ scope_out(2);
+ break;
+ }
+}
+
+/*
+ * Start a reset on a vioc after error (hopefully)
+ */
+vxstreset(vx)
+ register vx;
+{
+ register struct vx_softc *vs;
+ register struct vxdevice *vp;
+ register struct vxcmd *cp;
+ register int j;
+ extern int vxinreset();
+ int s;
+
+ s = spl8() ;
+ vs = &vx_softc[vx];
+ if (vs->vs_state == VXS_RESET) { /* avoid recursion */
+ splx(s);
+ return;
+ }
+ vp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ /*
+ * Zero out the vioc structures, mark the vioc as being
+ * reset, reinitialize the free command list, reset the vioc
+ * and start a timer to check on the progress of the reset.
+ */
+ bzero((caddr_t)vs, (unsigned)sizeof (*vs));
+
+ /*
+ * Setting VXS_RESET prevents others from issuing
+ * commands while allowing currently queued commands to
+ * be passed to the VIOC.
+ */
+ vs->vs_state = VXS_RESET;
+ /* init all cmd buffers */
+ for (j = 0; j < NVCXBUFS; j++) {
+ cp = &vs->vs_lst[j]; /* index a buffer */
+ cp->c_fwd = &vs->vs_lst[j+1]; /* point to next buf */
+ }
+ vs->vs_avail = &vs->vs_lst[0]; /* set idx to 1st free buf */
+ cp->c_fwd = (struct vxcmd *)0; /* mark last buf in free list */
+ printf("vx%d: reset...", vx);
+ vp->v_fault = 0;
+ vp->v_vioc = V_BSY;
+ vp->v_hdwre = V_RESET; /* reset interrupt */
+ timeout(vxinreset, (caddr_t)vx, hz*5);
+ splx(s);
+}
+
+/* continue processing a reset on a vioc after an error (hopefully) */
+vxinreset(vx)
+ int vx;
+{
+ register struct vxdevice *vp;
+ int s = spl8();
+
+ vp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ /*
+ * See if the vioc has reset.
+ */
+ if (vp->v_fault != VXF_READY) {
+ printf("failed\n");
+ splx(s);
+ return;
+ }
+ /*
+ * Send a LIDENT to the vioc and mess with carrier flags
+ * on parallel printer ports.
+ */
+ vxinit(vx, (long)0);
+ splx(s);
+}
+
+/*
+ * Restore modem control, parameters and restart output.
+ * Since the vioc can handle no more then 24 commands at a time
+ * and we could generate as many as 48 commands, we must do this in
+ * phases, issuing no more then 16 commands at a time.
+ */
+/* finish the reset on the vioc after an error (hopefully) */
+vxfnreset(vx, cp)
+ register int vx;
+ register struct vxcmd *cp;
+{
+ register struct vx_softc *vs;
+ register struct vxdevice *vp ;
+ register struct tty *tp, *tp0;
+ register int i;
+#ifdef notdef
+ register int on;
+#endif
+ extern int vxrestart();
+ int s = spl8();
+
+ vs = &vx_softc[vx];
+ vs->vs_loport = cp->par[5];
+ vs->vs_hiport = cp->par[7];
+ vrelease(vs, cp);
+ vs->vs_nbr = vx; /* assign VIOC-X board number */
+ vs->vs_state = VXS_READY;
+
+ vp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ vp->v_vcid = 0;
+
+ /*
+ * Restore modem information and control.
+ */
+ tp0 = &vx_tty[vx*16];
+ for (i = vs->vs_loport; i <= vs->vs_hiport; i++) {
+ tp = tp0 + i;
+ if (tp->t_state&(TS_ISOPEN|TS_WOPEN)) {
+ tp->t_state &= ~TS_CARR_ON;
+ vcmodem(tp->t_dev, VMOD_ON);
+ if (tp->t_state&TS_CARR_ON)
+ wakeup((caddr_t)&tp->t_canq);
+ else if (tp->t_state & TS_ISOPEN) {
+ ttyflush(tp, FREAD|FWRITE);
+ if (tp->t_state&TS_FLUSH)
+ wakeup((caddr_t)&tp->t_state);
+ if ((tp->t_flags&NOHANG) == 0) {
+ gsignal(tp->t_pgrp, SIGHUP);
+ gsignal(tp->t_pgrp, SIGCONT);
+ }
+ }
+ }
+ /*
+ * If carrier has changed while we were resetting,
+ * take appropriate action.
+ */
+#ifdef notdef
+ on = vp->v_dcd & 1<<i;
+ if (on && (tp->t_state&TS_CARR_ON) == 0) {
+ tp->t_state |= TS_CARR_ON;
+ wakeup((caddr_t)&tp->t_canq);
+ } else if (!on && tp->t_state&TS_CARR_ON) {
+ tp->t_state &= ~TS_CARR_ON;
+ if (tp->t_state & TS_ISOPEN) {
+ ttyflush(tp, FREAD|FWRITE);
+ if (tp->t_state&TS_FLUSH)
+ wakeup((caddr_t)&tp->t_state);
+ if ((tp->t_flags&NOHANG) == 0) {
+ gsignal(tp->t_pgrp, SIGHUP);
+ gsignal(tp->t_pgrp, SIGCONT);
+ }
+ }
+ }
+#endif
+ }
+ vs->vs_state = VXS_RESET;
+ timeout(vxrestart, (caddr_t)vx, hz);
+ splx(s);
+}
+
+/*
+ * Restore a particular aspect of the VIOC.
+ */
+vxrestart(vx)
+ int vx;
+{
+ register struct tty *tp, *tp0;
+ register struct vx_softc *vs;
+ register int i, cnt;
+ int s = spl8();
+
+ cnt = vx >> 8;
+ vx &= 0xff;
+ vs = &vx_softc[vx];
+ vs->vs_state = VXS_READY;
+ tp0 = &vx_tty[vx*16];
+ for (i = vs->vs_loport; i <= vs->vs_hiport; i++) {
+ tp = tp0 + i;
+ if (cnt != 0) {
+ tp->t_state &= ~(TS_BUSY|TS_TIMEOUT);
+ if (tp->t_state&(TS_ISOPEN|TS_WOPEN))
+ vxstart(tp); /* restart pending output */
+ } else {
+ if (tp->t_state&(TS_WOPEN|TS_ISOPEN))
+ vxcparam(tp->t_dev, 0);
+ }
+ }
+ if (cnt == 0) {
+ vs->vs_state = VXS_RESET;
+ timeout(vxrestart, (caddr_t)(vx + 1*256), hz);
+ } else
+ printf("done\n");
+ splx(s);
+}
+
+vxreset(dev)
+ dev_t dev;
+{
+
+ vxstreset(minor(dev) >> 4); /* completes asynchronously */
+}
+
+vxfreset(vx)
+ register int vx;
+{
+ struct vba_device *vi;
+
+ if ((unsigned)vx > NVX || (vi = vxinfo[vx]) == 0 || vi->ui_addr == 0)
+ return (ENODEV);
+ vx_softc[vx].vs_state = VXS_READY;
+ vxstreset(vx);
+ return (0); /* completes asynchronously */
+}
+
+vcmodem(dev, flag)
+ dev_t dev;
+{
+ struct tty *tp;
+ register struct vxcmd *cp;
+ register struct vx_softc *vs;
+ register struct vxdevice *kp;
+ register port;
+ int unit;
+
+ unit = minor(dev);
+ tp = &vx_tty[unit];
+ vs = (struct vx_softc *)tp->t_addr;
+ cp = vobtain(vs);
+ kp = (struct vxdevice *)((struct vba_device *)vxinfo[vs->vs_nbr])->ui_addr;
+
+ port = unit & 017;
+ /*
+ * Issue MODEM command
+ */
+ cp->cmd = VXC_MDMCTL;
+ cp->par[0] = (flag == VMOD_ON) ? V_ENAB : V_DISAB;
+ cp->par[1] = port;
+ vcmd(vs->vs_nbr, (caddr_t)&cp->cmd);
+ port -= vs->vs_loport;
+ if ((kp->v_dcd >> port) & 1) {
+ if (flag == VMOD_ON)
+ tp->t_state |= TS_CARR_ON;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * VCMINTR called when an unsolicited interrup occurs signaling
+ * some change of modem control state.
+ */
+vcmintr(vx)
+ register vx;
+{
+ register struct vxdevice *kp;
+ register struct tty *tp;
+ register port;
+
+ kp = (struct vxdevice *)((struct vba_device *)vxinfo[vx])->ui_addr;
+ port = kp->v_usdata[0] & 017;
+ tp = &vx_tty[vx*16+port];
+#if NVBSC > 0
+ /*
+ * Check for change in DSR for BISYNC port.
+ */
+ if (bscport[vx*16+port]&BISYNC) {
+ if (kp->v_ustat&DSR_CHG) {
+ register struct vx_softc *xp;
+ register struct bsc *bp;
+ extern struct bsc bsc[];
+
+ vs = (struct vx_softc *)tp->t_addr;
+ bp = &bsc[minor(tp->t_dev)] ;
+ bp->b_hlflgs &= ~BSC_DSR ;
+ if (kp->v_ustat & DSR_ON)
+ bp->b_hlflgs |= BSC_DSR ;
+ printf("BSC DSR Chg: %x\n", kp->v_ustat&DSR_CHG);/*XXX*/
+ }
+ return;
+ }
+#endif
+ if ((kp->v_ustat&DCD_ON) && ((tp->t_state&TS_CARR_ON) == 0)) {
+ tp->t_state |= TS_CARR_ON;
+ wakeup((caddr_t)&tp->t_canq);
+ return;
+ }
+ if ((kp->v_ustat&DCD_OFF) && (tp->t_state&TS_CARR_ON)) {
+ tp->t_state &= ~TS_CARR_ON;
+ if (tp->t_state&TS_ISOPEN) {
+ register struct vx_softc *vs;
+ register struct vcmds *cp;
+ register struct vxcmd *cmdp;
+
+ ttyflush(tp, FREAD|FWRITE);
+ /* clear all pending trnansmits */
+ vs = &vx_softc[vx];
+ if (tp->t_state&(TS_BUSY|TS_FLUSH) &&
+ vs->vs_vers == VXV_NEW) {
+ int i, cmdfound = 0;
+
+ cp = &vs->vs_cmds;
+ for (i = cp->v_empty; i != cp->v_fill; ) {
+ cmdp = (struct vxcmd *)((long *)cp->cmdbuf[i]-1);
+ if ((cmdp->cmd == VXC_XMITDTA ||
+ cmdp->cmd == VXC_XMITIMM) &&
+ ((struct vxmit *)cmdp->par)->line == port) {
+ cmdfound++;
+ cmdp->cmd = VXC_FDTATOX;
+ cmdp->par[1] = port;
+ }
+ if (++i >= VC_CMDBUFL)
+ i = 0;
+ }
+ if (cmdfound)
+ tp->t_state &= ~(TS_BUSY|TS_FLUSH);
+ /* cmd is already in vioc, have to flush it */
+ else {
+ cmdp = vobtain(vs);
+ cmdp->cmd = VXC_FDTATOX;
+ cmdp->par[1] = port;
+ vcmd(vx, (caddr_t)&cmdp->cmd);
+ }
+ }
+ if ((tp->t_flags&NOHANG) == 0) {
+ gsignal(tp->t_pgrp, SIGHUP);
+ gsignal(tp->t_pgrp, SIGCONT);
+ }
+ }
+ return;
+ }
+ if ((kp->v_ustat&BRK_CHR) && (tp->t_state&TS_ISOPEN)) {
+ (*linesw[tp->t_line].l_rint)(tp->t_intrc & 0377, tp);
+ return;
+ }