text: /*
   * Copyright (c) 1988 Regents of the University of California.
   * All rights reserved.
   *
   * This code is derived from software contributed to Berkeley by
   * Computer Consoles Inc.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   * 3. All advertising materials mentioning features or use of this software
   *    must display the following acknowledgement:
   *  This product includes software developed by the University of
   *  California, Berkeley and its contributors.
   * 4. Neither the name of the University nor the names of its contributors
   *    may be used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   *
   *  @(#)mp.c    7.17 (Berkeley) 5/16/91
   */
  
  #include "mp.h"
  #if NMP > 0
  /*
   * Multi Protocol Communications Controller (MPCC).
   * Asynchronous Terminal Protocol Support.
   */
  #include "sys/param.h"
  #include "sys/ioctl.h"
  #include "sys/tty.h"
  #include "sys/user.h"
  #include "sys/map.h"
  #include "sys/buf.h"
  #include "sys/conf.h"
  #include "sys/file.h"
  #include "sys/errno.h"
  #include "sys/syslog.h"
  #include "sys/vmmac.h"
  #include "sys/kernel.h"
  #include "sys/clist.h"
  
  #include "../include/pte.h"
  #include "../include/mtpr.h"
  
  #include "../vba/vbavar.h"
  #include "../vba/mpreg.h"
  
  #define MPCHUNK     16
  #define MPPORT(n)   ((n) & 0xf)
  #define MPUNIT(n)   ((n) >> 4)
  
  /*
   * Driver information for auto-configuration stuff.
   */
  int     mpprobe(), mpattach(), mpintr();
  struct  vba_device *mpinfo[NMP];
  long    mpstd[] = { 0 };
  struct  vba_driver mpdriver =
      { mpprobe, 0, mpattach, 0, mpstd, "mp", mpinfo };
  
  int mpstart();
  int mpparam();
  struct  mpevent *mpparam2();
  struct  mpevent *mp_getevent();
  
  /*
   * The following structure is needed to deal with mpcc's convoluted
   * method for locating it's mblok structures (hold your stomach).
   * When an mpcc is reset at boot time it searches host memory
   * looking for a string that says ``ThIs Is MpCc''.  The mpcc
   * then reads the structure to locate the pointer to it's mblok
   * structure (you can wretch now).
   */
  struct mpbogus {
      char    s[12];          /* `ThIs Is MpCc'' */
      u_char  status;
      u_char  unused;
      u_short magic;
      struct  mblok *mb;
      struct  mblok *mbloks[NMP]; /* can support at most 16 mpcc's */
  } mpbogus = { 'T','h','I','s',' ','I','s',' ','M','p','C','c' };
  
  /*
   * Software state per unit.
   */
  struct  mpsoftc {
      u_int   ms_ivec;            /* interrupt vector */
      u_int   ms_softCAR;         /* software carrier for async */
      struct  mblok *ms_mb;       /* mpcc status area */
      struct  vb_buf ms_buf;      /* vba resources for ms_mb */
      struct  hxmtl ms_hxl[MPMAXPORT];/* host transmit list */
      struct  asyncparam ms_async[MPMAXPORT][MPINSET];/* async structs */
      char    ms_cbuf[MPMAXPORT][MPOUTSET][CBSIZE];/* input character buffers */
  } mp_softc[NMP];
  
  struct  speedtab
  mpspeedtab[] = {
      9600,   M9600,  /* baud rate = 9600 */
      4800,   M4800,  /* baud rate = 4800 */
      2400,   M2400,  /* baud rate = 2400 */
      1800,   M1800,  /* baud rate = 1800 */
      1200,   M1200,  /* baud rate = 1200 */
      600,    M600,   /* baud rate = 600 */
      300,    M300,   /* baud rate = 300 */
      200,    M200,   /* baud rate = 200 */
      150,    M150,   /* baud rate = 150 */
      134,    M134_5, /* baud rate = 134.5 */
      110,    M110,   /* baud rate = 110 */
      75,     M75,    /* baud rate = 75 */
      50,     M50,    /* baud rate = 50 */
      0,      M0,     /* baud rate = 0 */
      2000,   M2000,  /* baud rate = 2000 */
      3600,   M3600,  /* baud rate = 3600 */
      7200,   M7200,  /* baud rate = 7200 */
      19200,  M19200, /* baud rate = 19,200 */
      24000,  M24000, /* baud rate = 24,000 */
      28400,  M28400, /* baud rate = 28,400 */
      37800,  M37800, /* baud rate = 37,800 */
      40300,  M40300, /* baud rate = 40,300 */
      48000,  M48000, /* baud rate = 48,000 */
      52000,  M52000, /* baud rate = 52,000 */
      56800,  M56800, /* baud rate = 56,800 */
      EXTA,   MEXTA,  /* baud rate = Ext A */
      EXTB,   MEXTB,  /* baud rate = Ext B */
      -1, -1,
  };
  
  struct  tty mp_tty[NMP*MPCHUNK];
  #ifndef lint
  int nmp = NMP*MPCHUNK;
  #endif
  
  int ttrstrt();
  
  mpprobe(reg, vi)
      caddr_t reg;
      struct vba_device *vi;
  {
      register int br, cvec;
      register struct mpsoftc *ms;
  
  #ifdef lint
      br = 0; cvec = br; br = cvec;
      mpintr(0);
      mpdlintr(0);
  #endif
      if (badaddr(reg, 2))
          return (0);
      ms = &mp_softc[vi->ui_unit];
      /*
       * Allocate page tables and mblok
       * structure (mblok in non-cached memory).
       */
      if (vbainit(&ms->ms_buf, sizeof (struct mblok), VB_32BIT) == 0) {
          printf("mp%d: vbainit failed\n", vi->ui_unit);
          return (0);
      }
      ms->ms_mb = (struct mblok *)ms->ms_buf.vb_rawbuf;
      ms->ms_ivec = MPINTRBASE + 2*vi->ui_unit;   /* XXX */
      br = 0x14, cvec = ms->ms_ivec;          /* XXX */
      return (sizeof (*reg));
  }
  
  mpattach(vi)
      register struct vba_device *vi;
  {
      register struct mpsoftc *ms = &mp_softc[vi->ui_unit];
  
      ms->ms_softCAR = vi->ui_flags;
      /*
       * Setup pointer to mblok, initialize bogus
       * status block used by mpcc to locate the pointer
       * and then poke the mpcc to get it to search host
       * memory to find mblok pointer.
       */
      mpbogus.mbloks[vi->ui_unit] = (struct mblok *)ms->ms_buf.vb_physbuf;
      *(short *)vi->ui_addr = 0x100;      /* magic */
  }
  
  /*
   * Open an mpcc port.
   */
  /* ARGSUSED */
  mpopen(dev, mode)
      dev_t dev;
  {
      register struct tty *tp;
      register struct mpsoftc *ms;
      int error, s, port, unit, mpu;
      struct vba_device *vi;
      struct mpport *mp;
      struct mpevent *ev;
  
      unit = minor(dev);
      mpu = MPUNIT(unit);
      if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0)
          return (ENXIO);
      tp = &mp_tty[unit];
      if (tp->t_state & TS_XCLUDE && u.u_uid != 0)
          return (EBUSY);
      ms = &mp_softc[mpu];
      port = MPPORT(unit);
      if (ms->ms_mb->mb_proto[port] != MPPROTO_ASYNC ||
          ms->ms_mb->mb_status != MP_OPOPEN)
          return (ENXIO);
      mp = &ms->ms_mb->mb_port[port];     /* host mpcc struct */
      s = spl8();
      /*
       * serialize open and close events
       */
      while ((mp->mp_flags & MP_PROGRESS) || ((tp->t_state & TS_WOPEN) &&
          !(mode&O_NONBLOCK) && !(tp->t_cflag&CLOCAL)))
          if (error = tsleep((caddr_t)&tp->t_canq, TTIPRI | PCATCH,
              ttopen, 0)) {
              splx(s);
              return (error);
          }
  restart:
      tp->t_state |= TS_WOPEN;
      tp->t_addr = (caddr_t)ms;
      tp->t_oproc = mpstart;
      tp->t_param = mpparam;
      tp->t_dev = dev;
      if ((tp->t_state & TS_ISOPEN) == 0) {
          ttychars(tp);
          if (tp->t_ispeed == 0) {
              tp->t_ispeed = TTYDEF_SPEED;
              tp->t_ospeed = TTYDEF_SPEED;
              tp->t_iflag = TTYDEF_IFLAG;
              tp->t_oflag = TTYDEF_OFLAG;
              tp->t_lflag = TTYDEF_LFLAG;
              tp->t_cflag = TTYDEF_CFLAG;
          }
          /*
           * Initialize port state: init MPCC interface
           * structures for port and setup modem control.
           */
          error = mpportinit(ms, mp, port);
          if (error)
              goto bad;
          ev = mpparam2(tp, &tp->t_termios);
          if (ev == 0) {
              error = ENOBUFS;
              goto bad;
          }
          mp->mp_flags |= MP_PROGRESS;
          mpcmd(ev, EVCMD_OPEN, 0, ms->ms_mb, port);
          /*
           * wait for port to start
           */
          while (mp->mp_proto != MPPROTO_ASYNC)
              if (error = tsleep((caddr_t)&tp->t_canq,
                  TTIPRI | PCATCH, ttopen, 0))
                  goto bad;
          ttsetwater(tp);
          mp->mp_flags &= ~MP_PROGRESS;
      }
      while ((mode&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 &&
          (tp->t_state & TS_CARR_ON) == 0) {
          if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
              ttopen, 0))
              goto bad;
          /*
           * a mpclose() might have disabled port. if so restart
           */
          if (mp->mp_proto != MPPROTO_ASYNC)
              goto restart;
          tp->t_state |= TS_WOPEN;
      }
      error = (*linesw[tp->t_line].l_open)(dev,tp);
  done:
      splx(s);
      /*
       * wakeup those processes waiting for the open to complete
       */
      wakeup((caddr_t)&tp->t_canq);
      return (error);
  bad:
      tp->t_state &= ~TS_WOPEN;
      goto done;
  }
  
  /*
   * Close an mpcc port.
   */
  /* ARGSUSED */
  mpclose(dev, flag)
      dev_t dev;
  {
      register struct tty *tp;
      register struct mpport *mp;
      register struct mpevent *ev;
      int s, port, unit, error = 0;
      struct mblok *mb;
  
      unit = minor(dev);
      tp = &mp_tty[unit];
      port = MPPORT(unit);
      mb = mp_softc[MPUNIT(unit)].ms_mb;
      mp = &mb->mb_port[port];
      s = spl8();
      if (mp->mp_flags & MP_PROGRESS) {
          if (mp->mp_flags & MP_REMBSY) {
              mp->mp_flags &= ~MP_REMBSY;
              splx(s);
              return (0);
          }
          while (mp->mp_flags & MP_PROGRESS)
              if (error = tsleep((caddr_t)&tp->t_canq,
                  TTIPRI | PCATCH, ttclos, 0)) {
                  splx(s);
                  return (error);
              }
      }
      mp->mp_flags |= MP_PROGRESS;
      (*linesw[tp->t_line].l_close)(tp, flag);
      ev = mp_getevent(mp, unit, 1);
      if (ev == 0) {
          error = ENOBUFS;
          mp->mp_flags &= ~MP_PROGRESS;
          goto out;
      }
      if (tp->t_state & TS_HUPCLS || (tp->t_state & TS_ISOPEN) == 0)
          mpmodem(unit, MMOD_OFF);
      else
          mpmodem(unit, MMOD_ON);
      mpcmd(ev, EVCMD_CLOSE, 0, mb, port);
      error = ttyclose(tp);
  out:
      if (mp->mp_flags & MP_REMBSY)
          mpclean(mb, port);
      else
          while (mp->mp_flags & MP_PROGRESS && error == 0)
              error = tsleep((caddr_t)&tp->t_canq, TTIPRI | PCATCH,
                  ttclos, 0);
      splx(s);
      return (error);
  }
  
  /*
   * Read from an mpcc port.
   */
  mpread(dev, uio, flag)
      dev_t dev;
      struct uio *uio;
  {
      struct tty *tp;
  
      tp = &mp_tty[minor(dev)];
      return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
  }
  
  /*
   * Write to an mpcc port.
   */
  mpwrite(dev, uio, flag)
      dev_t dev;
      struct uio *uio;
  {
      struct tty *tp;
  
      tp = &mp_tty[minor(dev)];
      return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
  }
  
  /*
   * Ioctl for a mpcc port
   */
  mpioctl(dev, cmd, data, flag)
      dev_t dev;
      caddr_t data;
  {
      register struct tty *tp;
      register struct mpsoftc *ms;
      register struct mpport *mp;
      register struct mpevent *ev;
      int s, port, error, unit;
      struct mblok *mb;
  
      unit = minor(dev);
      tp = &mp_tty[unit];
      ms = &mp_softc[MPUNIT(unit)];
      mb = ms->ms_mb;
      port = MPPORT(unit);
      mp = &mb->mb_port[port];
      if (mp->mp_proto != MPPROTO_ASYNC)
          return(ENXIO);
      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)
          return (error);
      switch (cmd) {
      case TIOCSBRK:          /* send break */
      case TIOCCBRK:          /* clear break */
          s = spl8();
          while (mp->mp_flags & MP_IOCTL) {
              if (error = tsleep((caddr_t)&tp->t_canq,
                  TTIPRI | PCATCH, ttyout, 0)) {
                  splx(s);
                  return (error);
              }
              if (mp->mp_proto != MPPROTO_ASYNC) {
                  splx(s);
                  return (ENXIO);
              }
          }
          ev = mp_getevent(mp, unit, 0);
          if (ev) {
              mp->mp_flags |= MP_IOCTL;
              mpcmd(ev, EVCMD_IOCTL,
                  (cmd == TIOCSBRK ? A_BRKON : A_BRKOFF), mb, port);
          } else
              error = ENOBUFS;
          splx(s);
          break;
      case TIOCSDTR:          /* set dtr control line */
          break;
      case TIOCCDTR:          /* clear dtr control line */
          break;
      default:
          error = ENOTTY;
          break;
      }
      return (error);
  }
  
  mpparam(tp, t)
      struct tty *tp;
      struct termios *t;
  {
      register struct mpevent *ev;
      int unit = minor(tp->t_dev);
      struct mpsoftc *ms = &mp_softc[MPUNIT(unit)];
      struct mblok *mb = ms->ms_mb;
  
      ev = mpparam2(tp, t);
      if (ev == 0)
          return (ENOBUFS);
      mpcmd(ev, EVCMD_IOCTL, A_CHGALL, mb, MPPORT(unit));
      return (0);
  }
  
  struct mpevent *
  mpparam2(tp, t)
      register struct tty *tp;
      struct termios *t;
  {
      register struct mpevent *ev;
      register struct mpport *mp;
      int unit = minor(tp->t_dev);
      struct mblok *mb;
      struct mpsoftc *ms;
      register struct asyncparam *asp;
      int port, speedcode;
  
      ms = &mp_softc[MPUNIT(unit)];
      mb = ms->ms_mb;
      port = MPPORT(unit);
      mp = &mb->mb_port[port];
      ev = mp_getevent(mp, unit, 0);  /* XXX */
      speedcode = ttspeedtab(t->c_ospeed, mpspeedtab);
      if (ev == 0 || speedcode < 0) {
  printf("mp mpunit %d port %d param2 failed ev: %x speed %d, wanted %d\n",
              MPUNIT(unit), port, ev, speedcode, t->c_ospeed);
          return (0); /* XXX */
      }
      /* YUCK */
      asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1];
      asp->ap_xon = t->c_cc[VSTART];
      asp->ap_xoff = t->c_cc[VSTOP];
      if (!(t->c_iflag&IXON) || (asp->ap_xon == _POSIX_VDISABLE) ||
          (asp->ap_xoff == _POSIX_VDISABLE))
          asp->ap_xena = MPA_DIS;
      else
          asp->ap_xena = MPA_ENA;
      asp->ap_xany = ((t->c_iflag & IXANY) ? MPA_ENA : MPA_DIS);
  #ifdef notnow
      if (t->t_cflag&CSIZE) == CS8) {
  #endif
          asp->ap_data = MPCHAR_8;
          asp->ap_parity = MPPAR_NONE;
  #ifdef notnow
      } else {
          asp->ap_data = MPCHAR_7;
          if ((t->c_flags & (EVENP|ODDP)) == ODDP) /* XXX */
              asp->ap_parity = MPPAR_ODD;
          else
              asp->ap_parity = MPPAR_EVEN;
      }
  #endif
      asp->ap_loop = MPA_DIS;     /* disable loopback */
      asp->ap_rtimer = A_RCVTIM;  /* default receive timer */
      if (t->c_ospeed == B110)
          asp->ap_stop = MPSTOP_2;
      else
          asp->ap_stop = MPSTOP_1;
      if (t->c_ospeed == 0) {
          tp->t_state |= TS_HUPCLS;
          setm(&asp->ap_modem, 0, DROP);
          seti(&asp->ap_intena, A_DCD);
          return (ev);
      }
      if (t->c_ospeed == EXTA || t->c_ospeed == EXTB)
          asp->ap_baud = M19200;
      else
          asp->ap_baud = speedcode;
      if (1 || ms->ms_softCAR & (1<<port)) /* XXX HARDWIRE FOR NOW */
          setm(&asp->ap_modem, A_DTR, ASSERT);
      else
          setm(&asp->ap_modem, A_DTR, AUTO);
      seti(&asp->ap_intena, A_DCD);
      return(ev);
  }
  
  mpstart(tp)
      register struct tty *tp;
  {
      register struct mpevent *ev;
      register struct mpport *mp;
      struct mblok *mb;
      struct mpsoftc *ms;
      int port, unit, xcnt, n, s, i;
      struct  hxmtl *hxp;
      struct clist outq;
  
      s = spl8();
      unit = minor(tp->t_dev);
      ms = &mp_softc[MPUNIT(unit)];
      mb = ms->ms_mb;
      port = MPPORT(unit);
      mp = &mb->mb_port[port];
      hxp = &ms->ms_hxl[port];
      xcnt = 0;
      outq = tp->t_outq;
      for (i = 0; i < MPXMIT; i++) {
          if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
              break;
          if (outq.c_cc <= tp->t_lowat) {
              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 (outq.c_cc == 0)
              break;
          /*
           * If we're not currently busy outputting,
           * and there is data to be output, set up
           * port transmit structure to send to mpcc.
           */
          if (1) /* || tp->t_flags & (RAW|LITOUT))  XXX FIX */
              n = ndqb(&outq, 0);
          else {
              n = ndqb(&outq, 0200);
              if (n == 0) {
                  if (xcnt > 0)
                      break;
                  n = getc(&outq);
                  timeout(ttrstrt, (caddr_t)tp, (n&0177)+6);
                  tp->t_state |= TS_TIMEOUT;
                  break;
              }
          }
          hxp->dblock[i] = (caddr_t)kvtophys(outq.c_cf);
          hxp->size[i] = n;
          xcnt++;     /* count of xmts to send */
          ndadvance(&outq, n);
      }
      /*
       * If data to send, poke mpcc.
       */
      if (xcnt) {
          ev = mp_getevent(mp, unit, 0);
          if (ev == 0) {
              tp->t_state &= ~(TS_BUSY|TS_TIMEOUT);
          } else {
              tp->t_state |= TS_BUSY;
              ev->ev_count = xcnt;
              mpcmd(ev, EVCMD_WRITE, 0, mb, MPPORT(unit));
          }
      }
      splx(s);
  }
  
  /*
   * Advance cc bytes from q  but don't free memory.
   */
  ndadvance(q, cc)
      register struct clist *q;
      register cc;
  {
      register struct cblock *bp;
      char *end;
      int rem, s;
  
      s = spltty();
      if (q->c_cc <= 0)
          goto out;
      while (cc>0 && q->c_cc) {
          bp = (struct cblock *)((int)q->c_cf & ~CROUND);
          if ((int)bp == (((int)q->c_cl-1) & ~CROUND)) {
              end = q->c_cl;
          } else {
              end = (char *)((int)bp + sizeof (struct cblock));
          }
          rem = end - q->c_cf;
          if (cc >= rem) {
              cc -= rem;
              q->c_cc -= rem;
              q->c_cf = bp->c_next->c_info;
          } else {
              q->c_cc -= cc;
              q->c_cf += cc;
              break;
          }
      }
      if (q->c_cc <= 0) {
          q->c_cf = q->c_cl = NULL;
          q->c_cc = 0;
      }
  out:
      splx(s);
  }
  
  /*
   * Stop output on a line, e.g. for ^S/^Q or output flush.
   */
  /* ARGSUSED */
  mpstop(tp, rw)
      register struct tty *tp;
      int rw;
  {
      register struct mpport *mp;
      register struct mpevent *ev;
      int unit = minor(tp->t_dev);
      int port;
      struct mblok *mb;
      int s;
  
      s = spl8();
      if (tp->t_state & TS_BUSY) {
          if ((tp->t_state & TS_TTSTOP) == 0) {
              tp->t_state |= TS_FLUSH;
              port = MPPORT(unit);
              mb = mp_softc[MPUNIT(unit)].ms_mb;
              mp = &mb->mb_port[port];
              ev = mp_getevent(mp, unit, 0);
              if (ev == 0) {
                  splx(s);
                  return;
              }
              mpcmd(ev, EVCMD_WRITE, A_FLUSH, mb, port);
          }
      }
      splx(s);
  }
  
  /*
   * Initialize an async port's MPCC state.
   */
  mpportinit(ms, mp, port)
      register struct mpsoftc *ms;
      register struct mpport *mp;
      int port;
  {
      register struct mpevent *ev;
      register int i;
      caddr_t ptr;
  
      mp->mp_on = mp->mp_off = 0;
      mp->mp_nextrcv = 0;
      mp->mp_flags = 0;
      ev = &mp->mp_recvq[0];
      for (i = 0; ev < &mp->mp_recvq[MPINSET]; ev++, i++) {
          ev->ev_status = EVSTATUS_FREE;
          ev->ev_cmd = 0;
          ev->ev_opts = 0;
          ev->ev_error = 0;
          ev->ev_flags = 0;
          ev->ev_count = 0;
          ev->ev_un.hxl = (struct hxmtl *) kvtophys(&ms->ms_hxl[port]);
          ev->ev_params = (caddr_t) kvtophys(&ms->ms_async[port][i]);
      }
      ev = &mp->mp_sendq[0];
      for (i = 0; ev < &mp->mp_sendq[MPOUTSET]; ev++, i++) {
          /* init so that L2 can't send any events */
          /* to host until open has completed      */
          ev->ev_status = EVSTATUS_FREE;
          ev->ev_cmd = 0;
          ev->ev_opts = 0;
          ev->ev_error = 0;
          ev->ev_flags = 0;
          ev->ev_count = 0;
          ptr = (caddr_t) &ms->ms_cbuf[port][i][0];
          ev->ev_un.rcvblk = (u_char *)kvtophys(ptr);
          ev->ev_params = (caddr_t) kvtophys(ptr);
      }
      return (0);
  }
  
  /*
   * Send an event to an mpcc.
   */
  mpcmd(ev, cmd, flags, mb, port)
      register struct mpevent *ev;
      struct mblok *mb;
  {
      int s;
  
      s = spl8();
      /* move host values to inbound entry */
      ev->ev_cmd = cmd;
      ev->ev_opts = flags;
      /* show event ready for mpcc */
      ev->ev_status = EVSTATUS_GO;
      mpintmpcc(mb, port);
      splx(s);
  }
  
  /*
   * Return the next available event entry for the indicated port.
   */
  struct mpevent *
  mp_getevent(mp, unit, cls_req)
      register struct mpport *mp;
      int unit;
      int cls_req;
  {
      register struct mpevent *ev;
      int i, s;
  
      s = spl8();
      ev = &mp->mp_recvq[mp->mp_on];
      if (ev->ev_status != EVSTATUS_FREE)
          goto bad;
      /*
       * If not a close request, verify one extra
       * event is available for closing the port.
       */
      if (!cls_req) {
          if ((i = mp->mp_on + 1) >= MPINSET)
              i = 0;
          if (mp->mp_recvq[i].ev_status != EVSTATUS_FREE)
              goto bad;
      }
      /* init inbound fields marking this entry as busy */
      ev->ev_cmd = 0;
      ev->ev_opts = 0;
      ev->ev_error = 0;
      ev->ev_flags = 0;
      ev->ev_count = 0;
      ev->ev_status = EVSTATUS_BUSY;
      /* adjust pointer to next available inbound entry */
      adjptr(mp->mp_on, MPINSET);
      splx(s);
      return (ev);
  bad:
      splx(s);
      log(LOG_ERR, "mp%d: port%d, out of events\n",
          MPUNIT(unit), MPPORT(unit));
      return ((struct mpevent *)0);
  }
  
  mpmodem(unit, flag)
      int unit, flag;
  {
      struct mpsoftc *ms = &mp_softc[MPUNIT(unit)];
      int port = MPPORT(unit);
      register struct mpport *mp;
      register struct asyncparam *asp;
  
      mp = &ms->ms_mb->mb_port[port];
      asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1];
      if (flag == MMOD_ON) {
          if (1 || ms->ms_softCAR & (1 << port))/* XXX HARDWIRE FOR NOW */
              setm(&asp->ap_modem, A_DTR, ASSERT);
          else
              setm(&asp->ap_modem, A_DTR, AUTO);
          seti(&asp->ap_intena, A_DCD);
      } else {
          setm(&asp->ap_modem, 0, DROP);
          seti(&asp->ap_intena, 0);
      }
  }
  
  /*
   * Set up the modem control structure according to mask.
   * Each set bit in the mask means assert the corresponding
   * modem control line, otherwise, it will be dropped.
   * RTS is special since it can either be asserted, dropped
   * or put in auto mode for auto modem control.
   */
  static
  setm(mc, mask, rts)
      register struct mdmctl *mc;
      register int mask;
  {
  
      mc->mc_rngdsr = (mask & A_RNGDSR) ? ASSERT : DROP;
      mc->mc_rate = (mask & A_RATE) ? ASSERT : DROP;
      mc->mc_dcd = (mask & A_DCD) ? ASSERT : DROP;
      mc->mc_sectx = (mask & A_SECTX) ? ASSERT : DROP;
      mc->mc_cts = (mask & A_CTS) ? ASSERT : DROP;
      mc->mc_secrx = (mask & A_SECRX) ? ASSERT : DROP;
      mc->mc_dtr = (mask & A_DTR) ? ASSERT : DROP;
      mc->mc_rts = rts;
  }
  
  /*
   * Set up the status change enable field from mask.
   * When a signal is enabled in this structure and
   * and a change in state on a corresponding modem
   * control line occurs, a status change event will
   * be delivered to the host.
   */
  static
  seti(mc, mask)
      register struct mdmctl *mc;
      register int mask;
  {
  
      mc->mc_rngdsr = (mask & A_RNGDSR) ? MDM_ON : MDM_OFF;
      mc->mc_rate = (mask & A_RATE) ? MDM_ON : MDM_OFF;
      mc->mc_dcd = (mask & A_DCD) ? MDM_ON : MDM_OFF;
      mc->mc_sectx = (mask & A_SECTX) ? MDM_ON : MDM_OFF;
      mc->mc_cts = (mask & A_CTS) ? MDM_ON : MDM_OFF;
      mc->mc_secrx = (mask & A_SECRX) ? MDM_ON : MDM_OFF;
      mc->mc_dtr = (mask & A_DTR) ? MDM_ON : MDM_OFF;
      mc->mc_rts = (mask & A_RTS) ? MDM_ON : MDM_OFF;
  }
  
  mpcleanport(mb, port)
      struct mblok *mb;
      int port;
  {
      register struct mpport *mp;
      register struct tty *tp;
  
      mp = &mb->mb_port[port];
      if (mp->mp_proto == MPPROTO_ASYNC) {
          mp->mp_flags = MP_REMBSY;
          /* signal loss of carrier and close */
          tp = &mp_tty[mb->mb_unit*MPCHUNK+port];
          ttyflush(tp, FREAD|FWRITE);
          (void) (*linesw[tp->t_line].l_modem)(tp, 0);
      }
  }
  
  mpclean(mb, port)
      register struct mblok *mb;
      int port;
  {
      register struct mpport *mp;
      register struct mpevent *ev;
      register int i;
      u_char list[2];
      int unit;
  
      mp = &mb->mb_port[port];
      unit = mb->mb_unit;
      for (i = mp->mp_off; i != mp->mp_on; i = (i+1 % MPINSET)) {
          ev = &mp->mp_recvq[i];
          ev->ev_error = ENXIO;
          ev->ev_status = EVSTATUS_DONE;
      }
      list[0] = port, list[1] = MPPORT_EOL;
      mpxintr(unit, list);
      mprintr(unit, list);
      /* Clear async for port */
      mp->mp_proto = MPPROTO_UNUSED;
      mp->mp_flags = 0;
      mp->mp_on = 0;
      mp->mp_off = 0;
      mp->mp_nextrcv = 0;
  
      mp_tty[unit*MPCHUNK + port].t_state = 0;
      for (ev = &mp->mp_sendq[0]; ev < &mp->mp_sendq[MPOUTSET]; ev++) {
          ev->ev_status = EVSTATUS_FREE;
          ev->ev_cmd = 0;
          ev->ev_error = 0;
          ev->ev_un.rcvblk = 0;
          ev->ev_params = 0;
      }
      for (ev = &mp->mp_recvq[0]; ev < &mp->mp_recvq[MPINSET]; ev++) {
          ev->ev_status = EVSTATUS_FREE;
          ev->ev_cmd = 0;
          ev->ev_error = 0;
          ev->ev_params = 0;
      }
  }
  
  /*
   * MPCC interrupt handler.
   */
  mpintr(mpcc)
      int mpcc;
  {
      register struct mblok *mb;
      register struct his *his;
  
      mb = mp_softc[mpcc].ms_mb;
      if (mb == 0) {
          printf("mp%d: stray interrupt\n", mpcc);
          return;
      }
      his = &mb->mb_hostint;
      his->semaphore &= ~MPSEMA_AVAILABLE;
      /*
       * Check for events to be processed.
       */
      if (his->proto[MPPROTO_ASYNC].outbdone[0] != MPPORT_EOL)
          mprintr(mpcc, his->proto[MPPROTO_ASYNC].outbdone);
      if (his->proto[MPPROTO_ASYNC].inbdone[0] != MPPORT_EOL)
          mpxintr(mpcc, his->proto[MPPROTO_ASYNC].inbdone);
      if (mb->mb_harderr || mb->mb_softerr)
          mperror(mb, mpcc);
      his->semaphore |= MPSEMA_AVAILABLE;
  }
  
  /*
   * Handler for processing completion of transmitted events.
   */
  mpxintr(unit, list)
      register u_char *list;
  {
      register struct mpport *mp;
      register struct mpevent *ev;
      register struct mblok *mb;
      register struct tty *tp;
      register struct asyncparam *ap;
      struct mpsoftc *ms;
      int port, i, j;
  #   define nextevent(mp) &mp->mp_recvq[mp->mp_off]
  
      ms = &mp_softc[unit];
      mb = mp_softc[unit].ms_mb;
      for (j = 0; j < MPMAXPORT && ((port = *list++) != MPPORT_EOL); j++) {
          /*
           * Process each completed entry in the inbound queue.
           */
          mp = &mb->mb_port[port];
          tp = &mp_tty[unit*MPCHUNK + port];
          ev = nextevent(mp);
          for (; ev->ev_status & EVSTATUS_DONE; ev = nextevent(mp)) {
              /* YUCK */
              ap = &ms->ms_async[port][mp->mp_off];
              mppurge((caddr_t)ap, (int)sizeof (*ap));
              switch (ev->ev_cmd) {
              case EVCMD_OPEN:
                  /*
                   * Open completion, start all reads and
                   * assert modem status information.
                   */
                  for (i = 0; i < MPOUTSET; i++)
                      mp->mp_sendq[i].ev_status = EVSTATUS_GO;
                  (*linesw[tp->t_line].l_modem)
                      (tp, ap->ap_modem.mc_dcd == ASSERT);
                  mp_freein(ev);
                  adjptr(mp->mp_off, MPINSET);
                  mp->mp_proto = MPPROTO_ASYNC;   /* XXX */
                  wakeup((caddr_t)&tp->t_canq);
                  break;
              case EVCMD_CLOSE:
                  /*
                   * Close completion, flush all pending
                   * transmissions, free resources, and
                   * cleanup mpcc port state.
                   */
                  for (i = 0; i < MPOUTSET; i++) {
                      mp->mp_sendq[i].ev_status =
                          EVSTATUS_FREE;
                      mp->mp_sendq[i].ev_un.rcvblk = 0;
                      mp->mp_sendq[i].ev_params = 0;
                  }
                  mp_freein(ev);
                  adjptr(mp->mp_off, MPINSET);
                  tp->t_state &= ~(TS_CARR_ON|TS_BUSY|TS_FLUSH);
                  mp->mp_on = mp->mp_off = mp->mp_nextrcv = 0;
                  mp->mp_flags &= ~MP_PROGRESS;
                  mp->mp_proto = MPPROTO_UNUSED;
                  wakeup((caddr_t)&tp->t_canq);
                  break;
              case EVCMD_IOCTL:
                  mp_freein(ev);
                  adjptr(mp->mp_off, MPINSET);
                  mp->mp_flags &= ~MP_IOCTL;
                  wakeup((caddr_t)&tp->t_canq);
                  break;
              case EVCMD_WRITE:
                  /*
                   * Transmission completed, update tty
                   * state and restart output.
                   */
                  if (ev->ev_opts != A_FLUSH) {
                      tp->t_state &= ~TS_BUSY;
                      if (tp->t_state & TS_FLUSH)
                          tp->t_state &= ~TS_FLUSH;
                      else {
                          register int cc = 0, n;
                          struct hxmtl *hxp;
  
                          hxp = &ms->ms_hxl[port];
                          for (n=0;n < ev->ev_count; n++)
                              cc += hxp->size[n];
                          ndflush(&tp->t_outq, cc);
                      }
                  }
                  switch (ev->ev_error) {
                  case A_SIZERR:  /*# error in xmt data size */
                      mplog(unit, port, A_XSIZE, 0);
                      break;
                  case A_NXBERR:  /*# no more xmt evt buffers */
                      mplog(unit, port, A_NOXBUF, 0);
                      break;
                  }
                  mp_freein(ev);
                  adjptr(mp->mp_off, MPINSET);
                  mpstart(tp);
                  break;
              default:
                  mplog(unit, port, A_INVCMD, (int)ev->ev_cmd);
                  mp_freein(ev);
                  adjptr(mp->mp_off, MPINSET);
                  break;
              }
          }
      }
  #undef  nextevent
  }
  
  mp_freein(ev)
      register struct mpevent *ev;
  {
      /* re-init all values in this entry */
      ev->ev_cmd = 0;
      ev->ev_opts = 0;
      ev->ev_error = 0;
      ev->ev_flags = 0;
      ev->ev_count = 0;
      /* show this entry is available for use */
      ev->ev_status = EVSTATUS_FREE;
  }
  
  /*
   * Handler for processing received events.
   */
  mprintr(unit, list)
      u_char *list;
  {
      register struct tty *tp;
      register struct mpport *mp;
      register struct mpevent *ev;
      struct mblok *mb;
      register int cc;
      register char *cp;
      struct mpsoftc *ms;
      caddr_t ptr;
      char *rcverr;
      int port, i;
  
      ms = &mp_softc[unit];
      mb = mp_softc[unit].ms_mb;
      for (i = 0; i < MPMAXPORT && (port = *list++) != MPPORT_EOL; i++) {
          tp = &mp_tty[unit*MPCHUNK + port];
          mp = &mb->mb_port[port];
          ev = &mp->mp_sendq[mp->mp_nextrcv];
          while (ev->ev_status & EVSTATUS_DONE) {
              switch(ev->ev_cmd) {
              case EVCMD_STATUS:
                  /*
                   * Status change, look for carrier changes.
                   */
                  switch(ev->ev_opts) {
                  case DCDASRT:
                      (*linesw[tp->t_line].l_modem)(tp, 1);
                      wakeup((caddr_t)&tp->t_canq);
                      break;
                  case DCDDROP:
                      (*linesw[tp->t_line].l_modem)(tp, 0);
                      wakeup((caddr_t)&tp->t_canq);
                      break;
                  case NORBUF:
                  case NOEBUF:
                      mplog(unit, port,
                          "out of receive events", 0);
                      break;
                  default:
                      mplog(unit, port,
                          "unexpect status command",
                          (int)ev->ev_opts);
                      break;
                  }
                  break;
              case EVCMD_READ:
                  /*
                   * Process received data.
                   */
                  if ((tp->t_state & TS_ISOPEN) == 0) {
                      wakeup((caddr_t)&tp->t_rawq);
                      break;
                  }
                  if ((cc = ev->ev_count) == 0)
                      break;
                  cp = ms->ms_cbuf[port][mp->mp_nextrcv];
                  mppurge(cp, CBSIZE);
                  while (cc-- > 0) {
                      /*
                       * A null character is inserted,
                       * potentially when a break or framing
                       * error occurs. If we're not in raw
                       * mode, substitute the interrupt
                       * character.
                       */
                      /*** XXX - FIXUP ***/
                      if (*cp == 0 &&
                          (ev->ev_error == BRKASRT ||
                           ev->ev_error == FRAMERR))
                          if ((tp->t_flags&RAW) == 0)
                              ;
                              /* XXX was break */
                      (*linesw[tp->t_line].l_rint)(*cp++, tp);
                  }
                  /* setup for next read */
                  ptr = (caddr_t)&mp_softc[unit].ms_cbuf[port][mp->mp_nextrcv][0];
                  ev->ev_un.rcvblk = (u_char *)kvtophys(ptr);
                  ev->ev_params = (caddr_t) kvtophys(ptr);
                  switch(ev->ev_error) {
                  case RCVDTA:
                      /* Normal (good) rcv data do not
                       * report the following they are
                       * "normal" errors
                       */
                  case FRAMERR:
                      /* frame error */
                  case BRKASRT:
                      /* Break condition */
                  case PARERR:
                      /* parity error */
                      rcverr = (char *)0;
                      break;
                  case OVRNERR:
                      /* Overrun error */
                      rcverr = "overrun error";
                      break;
                  case OVFERR:
                      /* Overflow error */
                      rcverr = "overflow error";
                      break;
                  default:
                      rcverr = "undefined rcv error";
                      break;
                  }
                  if (rcverr != (char *)0)
                      mplog(unit, port, rcverr,
                            (int)ev->ev_error);
                  break;
              default:
                  mplog(unit, port, "unexpected command",
                      (int)ev->ev_cmd);
                  break;
              }
              ev->ev_cmd = 0;
              ev->ev_opts = 0;
              ev->ev_error = 0;
              ev->ev_flags = 0;
              ev->ev_count = 0;
              ev->ev_status = EVSTATUS_GO;    /* start next read */
              adjptr(mp->mp_nextrcv, MPOUTSET);
              ev = &mp->mp_sendq[mp->mp_nextrcv];
          }
      }
  }
  
  /*
   * Log an mpcc diagnostic.
   */
  mplog(unit, port, cp, flags)
      char *cp;
  {
  
      if (flags)
          log(LOG_ERR, "mp%d: port%d, %s (%d)\n",
              unit, port, cp, flags);
      else
          log(LOG_ERR, "mp%d: port%d, %s\n", unit, port, cp);
  }
  
  int MPHOSTINT = 1;
  
  mptimeint(mb)
      register struct mblok *mb;
  {
  
      mb->mb_mpintcnt = 0;
      mb->mb_mpintclk = (caddr_t)0;
      *(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2;
  }
  
  /*
   * Interupt mpcc
   */
  mpintmpcc(mb, port)
      register struct mblok *mb;
  {
  
      mb->mb_intr[port] |= MPSEMA_WORK;
      if (++mb->mb_mpintcnt == MPHOSTINT) {
          mb->mb_mpintcnt = 0;
          *(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2;
          if (mb->mb_mpintclk) {
              untimeout(mptimeint, (caddr_t)mb);
              mb->mb_mpintclk = 0;
          }
      } else {
          if (mb->mb_mpintclk == 0) {
              timeout(mptimeint, (caddr_t)mb, 4);
              mb->mb_mpintclk = (caddr_t)1;
          }
      }
  }
  
  static char *mpherrmsg[] = {
      "",
      "Bus error",                        /* MPBUSERR */
      "Address error",                    /* ADDRERR */
      "Undefined ecc interrupt",          /* UNDECC */
      "Undefined interrupt",              /* UNDINT */
      "Power failure occurred",           /* PWRFL */
      "Stray transmit done interrupt",    /* NOXENTRY */
      "Two fast timers on one port",      /* TWOFTMRS */
      "Interrupt queue full",             /* INTQFULL */
      "Interrupt queue ack error",        /* INTQERR */
      "Uncorrectable dma parity error",   /* CBPERR */
      "32 port ACAP failed power up",     /* ACPDEAD */
  };
  #define NHERRS  (sizeof (mpherrmsg) / sizeof (mpherrmsg[0]))
  
  mperror(mb, unit)
      register struct mblok *mb;
      int unit;
  {
      register char *cp;
      register int i;
  
      if (mb->mb_softerr) {
          switch (mb->mb_softerr) {
          case DMAPERR:   /* dma parity error */
              cp = "dma parity error";
              break;
          case ECCERR:
              cp = "local memory ecc error";
              break;
          default:
              cp = "unknown error";
              break;
          }
          log(LOG_ERR, "mp%d: soft error, %s", unit, cp);
          mb->mb_softerr = 0;
      }
      if (mb->mb_harderr) {
          if (mb->mb_harderr < NHERRS)
              cp = mpherrmsg[mb->mb_harderr];
          else
              cp = "unknown error";
          log(LOG_ERR, "mp%d: hard error, %s", unit, cp);
          if (mb->mb_status == MP_OPOPEN) {
              for (i = 0; i < MPMAXPORT; i++) {
                  mpcleanport(mb, i);
                  mb->mb_proto[i] = MPPROTO_UNUSED;
              }
          }
          mb->mb_harderr = 0;
          mb->mb_status = 0;
      }
  }
  
  mppurge(addr, cc)
      register caddr_t addr;
      register int cc;
  {
  
      for (; cc >= 0; addr += NBPG, cc -= NBPG)
          mtpr(P1DC, addr);
  }
  
  /*
   * MPCC Download Pseudo-device.
   */
  char    mpdlbuf[MPDLBUFSIZE];
  int mpdlbusy;       /* interlock on download buffer */
  int mpdlerr;
  
  mpdlopen(dev)
      dev_t dev;
  {
      int unit, mpu;
      struct vba_device *vi;
  
      unit = minor(dev);
      mpu = MPUNIT(unit);
      if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0)
          return (ENODEV);
      return (0);
  }
  
  mpdlwrite(dev, uio)
      dev_t dev;
      struct uio *uio;
  {
      register struct mpsoftc *ms = &mp_softc[MPUNIT(minor(dev))];
      register struct mpdl *dl;
      int error;
  
      if (ms->ms_mb == 0 || ms->ms_mb->mb_status != MP_DLOPEN)
          return (EFAULT);
      dl = &ms->ms_mb->mb_dl;
      dl->mpdl_count = uio->uio_iov->iov_len;
      dl->mpdl_data = (caddr_t) kvtophys(mpdlbuf);
      if (error = uiomove(mpdlbuf, (int)dl->mpdl_count, uio))
          return (error);
      uio->uio_resid -= dl->mpdl_count;    /* set up return from write */
      dl->mpdl_cmd = MPDLCMD_NORMAL;
      error = mpdlwait(dl);
      return (error);
  }
  
  mpdlclose(dev)
      dev_t dev;
  {
      register struct mblok *mb = mp_softc[MPUNIT(minor(dev))].ms_mb;
  
      if (mb == 0 || mb->mb_status != MP_DLDONE) {
          mpbogus.status = 0;
          if (mpbogus.mb == mpbogus.mbloks[MPUNIT(minor(dev))])
              mpdlbusy--;
          return (EEXIST);
      }
      mb->mb_status = MP_OPOPEN;
      mpbogus.status = 0;
      /* set to dead, for board handshake */
      mb->mb_hostint.imok = MPIMOK_DEAD;
      return (0);
  }
  
  /* ARGSUSED */
  mpdlioctl(dev, cmd, data, flag)
      dev_t dev;
      caddr_t data;
  {
      register struct mblok *mb;
      register struct mpdl *dl;
      int unit, error = 0, s, i;
  
      mb = mp_softc[unit=MPUNIT(minor(dev))].ms_mb;
      if (mb == 0)
           return (EEXIST);
      dl = &mb->mb_dl;
      error = 0;
      switch (cmd) {
      case MPIOPORTMAP:
          bcopy(data, (caddr_t)mb->mb_proto, sizeof (mb->mb_proto));
          break;
      case MPIOHILO:
          bcopy(data, (caddr_t)&mb->mb_hiport, 2*(sizeof(mb->mb_hiport)));
          break;
      case MPIOENDDL:
          dl->mpdl_count = 0;
          dl->mpdl_data = 0;
          dl->mpdl_cmd = MPIOENDDL&IOCPARM_MASK;
          error = mpdlwait(dl);
          mpccinit(unit);
          mb->mb_status = MP_DLDONE;
          mpdlbusy--;
          break;
      case MPIOENDCODE:
          dl->mpdl_count = 0;
          dl->mpdl_data = 0;
          dl->mpdl_cmd = MPIOENDCODE&IOCPARM_MASK;
          error = mpdlwait(dl);
          break;
      case MPIOASYNCNF:
          bcopy(data, mpdlbuf, sizeof (struct abdcf));
          dl->mpdl_data = (caddr_t) kvtophys(mpdlbuf);
          dl->mpdl_count = sizeof (struct abdcf);
          dl->mpdl_cmd = MPIOASYNCNF&IOCPARM_MASK;
          error = mpdlwait(dl);
          break;
      case MPIOSTARTDL:
          s = spl8();
          while (mpdlbusy)
              if (error = tsleep((caddr_t)&mpdlbusy,
                  (PZERO+1) | PCATCH, devioc, 0))
                  break;
          splx(s);
          if (error)
              break;
          mpdlbusy++;
          /* initialize the downloading interface */
          mpbogus.magic = MPMAGIC;
          mpbogus.mb = mpbogus.mbloks[unit];
          mpbogus.status = 1;
          dl->mpdl_status = EVSTATUS_FREE;
          dl->mpdl_count = 0;
          dl->mpdl_cmd = 0;
          dl->mpdl_data = (char *) 0;
          mpdlerr = 0;
          mb->mb_magic = MPMAGIC;
          mb->mb_ivec = mp_softc[unit].ms_ivec+1; /* download vector */
          mb->mb_status = MP_DLPEND;
          mb->mb_diagswitch[0] = 'A';
          mb->mb_diagswitch[1] = 'P';
          s = spl8();
          *(u_short *)mpinfo[unit]->ui_addr = 2;
          error = tsleep((caddr_t)&mb->mb_status, (PZERO+1) | PCATCH,
              devio, 30*hz);
          splx(s);
          if (error == EWOULDBLOCK)
              error = ETIMEDOUT;
          if (error)
              mpbogus.status = 0;
          bzero((caddr_t)mb->mb_port, sizeof (mb->mb_port));
          break;
      case MPIORESETBOARD:
          s = spl8();
          if (mb->mb_imokclk)
              mb->mb_imokclk = 0;
          *(u_short *)mpinfo[unit]->ui_addr = 0x100;
          if (mb->mb_status == MP_DLOPEN || mb->mb_status == MP_DLDONE) {
              mpdlerr = MP_DLERROR;
              dl->mpdl_status = EVSTATUS_FREE;
              wakeup((caddr_t)&dl->mpdl_status);
              mpbogus.status = 0;
          }
          for (i = 0; i < MPMAXPORT; i++) {
              if (mb->mb_harderr || mb->mb_softerr)
                  mperror(mb, i);
              mpcleanport(mb, i);
              mb->mb_proto[i] = MPPROTO_UNUSED;
          }
          mb->mb_status = 0;
          splx(s);
          break;
      default:
          error = EINVAL;
          break;
      }
      return (error);
  }
  
  mpccinit(unit)
      int unit;
  {
      register struct mblok *mb = mp_softc[unit].ms_mb;
      register struct his *his;
      register int i, j;
  
      mb->mb_status = MP_DLDONE;
      mb->mb_ivec = mp_softc[unit].ms_ivec;
      mb->mb_magic = MPMAGIC;
      /* Init host interface structure */
      his = &mb->mb_hostint;
      his->semaphore = MPSEMA_AVAILABLE;
      for (i = 0; i < NMPPROTO; i++)
          for (j = 0; j < MPMAXPORT; j++) {
              his->proto[i].inbdone[j] = MPPORT_EOL;
              his->proto[i].outbdone[j] = MPPORT_EOL;
          }
      mb->mb_unit = unit;
  }
  
  mpdlintr(mpcc)
      int mpcc;
  {
      register struct mblok *mb;
      register struct mpdl *dl;
  
      mb = mp_softc[mpcc].ms_mb;
      if (mb == 0) {
          printf("mp%d: stray download interrupt\n", mpcc);
          return;
      }
      dl = &mb->mb_dl;
      switch (mb->mb_status) {
      case MP_DLOPEN:
          if (dl->mpdl_status != EVSTATUS_DONE)
              mpdlerr = MP_DLERROR;
          dl->mpdl_status = EVSTATUS_FREE;
          wakeup((caddr_t)&dl->mpdl_status);
          return;
      case MP_DLPEND:
          mb->mb_status = MP_DLOPEN;
          wakeup((caddr_t)&mb->mb_status);
          /* fall thru... */
      case MP_DLTIME:
          return;
      case MP_OPOPEN:
          if (mb->mb_imokclk)
              mb->mb_imokclk = 0;
          mb->mb_nointcnt = 0;        /* reset no interrupt count */
          mb->mb_hostint.imok = MPIMOK_DEAD;
          mb->mb_imokclk = (caddr_t)1;
          break;
      default:
          log(LOG_ERR, "mp%d: mpdlintr, status %x\n",
              mpcc, mb->mb_status);
          break;
      }
  }
  
  /*
   * Wait for a transfer to complete or a timeout to occur.
   */
  mpdlwait(dl)
      register struct mpdl *dl;
  {
      int s, error = 0;
  
      s = spl8();
      dl->mpdl_status = EVSTATUS_GO;
      while (dl->mpdl_status != EVSTATUS_FREE) {
          error = tsleep((caddr_t)&dl->mpdl_status, (PZERO+1) | PCATCH,
              devout, 0);
          if (mpdlerr == MP_DLERROR)
              error = EIO;
          if (error)
              break;
      }
      splx(s);
      return (error);
  }
  #endif
  