1*32681Skarels /* mp.c 1.2 87/11/24 */ 232633Ssam 332633Ssam #include "mp.h" 432633Ssam #if NMP > 0 532633Ssam /* 632633Ssam * Multi Protocol Communications Controller (MPCC). 732633Ssam * Asynchronous Terminal Protocol Support. 832633Ssam */ 932633Ssam #include "../machine/pte.h" 1032633Ssam #include "../machine/mtpr.h" 1132633Ssam 1232633Ssam #include "param.h" 1332633Ssam #include "ioctl.h" 1432633Ssam #include "tty.h" 1532633Ssam #include "dir.h" 1632633Ssam #include "user.h" 1732633Ssam #include "map.h" 1832633Ssam #include "buf.h" 1932633Ssam #include "conf.h" 2032633Ssam #include "file.h" 2132633Ssam #include "uio.h" 2232633Ssam #include "errno.h" 2332633Ssam #include "syslog.h" 2432633Ssam #include "vmmac.h" 2532633Ssam #include "kernel.h" 2632633Ssam #include "clist.h" 2732633Ssam 2832633Ssam #include "../tahoevba/vbavar.h" 2932633Ssam #include "../tahoevba/mpreg.h" 3032633Ssam 3132633Ssam #define MPCHUNK 16 3232633Ssam #define MPPORT(n) ((n) & 0xf) 3332633Ssam #define MPUNIT(n) ((n) >> 4) 3432633Ssam 3532633Ssam /* 3632633Ssam * Driver information for auto-configuration stuff. 3732633Ssam */ 3832633Ssam int mpprobe(), mpattach(), mpintr(); 3932633Ssam struct vba_device *mpinfo[NMP]; 4032633Ssam long mpstd[] = { 0 }; 4132633Ssam struct vba_driver mpdriver = 4232633Ssam { mpprobe, 0, mpattach, 0, mpstd, "mp", mpinfo }; 4332633Ssam 4432633Ssam int mpstart(); 4532633Ssam struct mpevent *mpparam(); 4632633Ssam struct mpevent *mp_getevent(); 4732633Ssam 4832633Ssam /* 4932633Ssam * The following structure is needed to deal with mpcc's convoluted 5032633Ssam * method for locating it's mblok structures (hold your stomach). 5132633Ssam * When an mpcc is reset at boot time it searches host memory 5232633Ssam * looking for a string that says ``ThIs Is MpCc''. The mpcc 5332633Ssam * then reads the structure to locate the pointer to it's mblok 5432633Ssam * structure (you can wretch now). 5532633Ssam */ 5632633Ssam struct mpbogus { 5732633Ssam char s[12]; /* `ThIs Is MpCc'' */ 5832633Ssam u_char status; 5932633Ssam u_char unused; 6032633Ssam u_short magic; 6132633Ssam struct mblok *mb; 6232633Ssam struct mblok *mbloks[NMP]; /* can support at most 16 mpcc's */ 6332633Ssam } mpbogus = { 'T','h','I','s',' ','I','s',' ','M','p','C','c' }; 6432633Ssam 6532633Ssam /* 6632633Ssam * Software state per unit. 6732633Ssam */ 6832633Ssam struct mpsoftc { 6932633Ssam u_int ms_ivec; /* interrupt vector */ 7032633Ssam u_int ms_softCAR; /* software carrier for async */ 7132633Ssam struct mblok *ms_mb; /* mpcc status area */ 7232633Ssam struct vb_buf ms_buf; /* vba resources for ms_mb */ 7332633Ssam struct hxmtl ms_hxl[MPMAXPORT];/* host transmit list */ 7432633Ssam struct asyncparam ms_async[MPMAXPORT][MPINSET];/* async structs */ 7532633Ssam char ms_cbuf[MPMAXPORT][MPOUTSET][CBSIZE];/* input character buffers */ 7632633Ssam } mp_softc[NMP]; 7732633Ssam 7832633Ssam struct tty mp_tty[NMP*MPCHUNK]; 7932633Ssam #ifndef lint 8032633Ssam int nmp = NMP*MPCHUNK; 8132633Ssam #endif 8232633Ssam 8332633Ssam int ttrstrt(); 8432633Ssam 8532633Ssam mpprobe(reg, vi) 8632633Ssam caddr_t reg; 8732633Ssam struct vba_device *vi; 8832633Ssam { 8932633Ssam register int br, cvec; 9032633Ssam register struct mpsoftc *ms; 9132633Ssam 9232633Ssam #ifdef lint 9332633Ssam br = 0; cvec = br; br = cvec; 9432633Ssam mpintr(0); 9532633Ssam #endif 9632633Ssam if (badaddr(reg, 2)) 9732633Ssam return (0); 9832633Ssam ms = &mp_softc[vi->ui_unit]; 9932633Ssam /* 10032633Ssam * Allocate page tables and mblok 10132633Ssam * structure (mblok in non-cached memory). 10232633Ssam */ 10332633Ssam if (vbainit(&ms->ms_buf, sizeof (struct mblok), VB_32BIT) == 0) { 10432633Ssam printf("mp%d: vbainit failed\n", vi->ui_unit); 10532633Ssam return (0); 10632633Ssam } 10732633Ssam ms->ms_mb = (struct mblok *)ms->ms_buf.vb_rawbuf; 10832633Ssam ms->ms_ivec = MPINTRBASE + 2*vi->ui_unit; /* XXX */ 10932633Ssam br = 0x14, cvec = ms->ms_ivec; /* XXX */ 11032633Ssam return (sizeof (struct mblok)); 11132633Ssam } 11232633Ssam 11332633Ssam mpattach(vi) 11432633Ssam register struct vba_device *vi; 11532633Ssam { 11632633Ssam register struct mpsoftc *ms = &mp_softc[vi->ui_unit]; 11732633Ssam 11832633Ssam ms->ms_softCAR = vi->ui_flags; 11932633Ssam /* 12032633Ssam * Setup pointer to mblok, initialize bogus 12132633Ssam * status block used by mpcc to locate the pointer 12232633Ssam * and then poke the mpcc to get it to search host 12332633Ssam * memory to find mblok pointer. 12432633Ssam */ 12532633Ssam mpbogus.mbloks[vi->ui_unit] = (struct mblok *)ms->ms_buf.vb_physbuf; 12632633Ssam *(short *)vi->ui_addr = 0x100; /* magic */ 12732633Ssam } 12832633Ssam 12932633Ssam /* 13032633Ssam * Open an mpcc port. 13132633Ssam */ 13232633Ssam mpopen(dev, mode) 13332633Ssam dev_t dev; 13432633Ssam { 13532633Ssam register struct tty *tp; 13632633Ssam register struct mpsoftc *ms; 13732633Ssam int error, s, port, unit, mpu; 13832633Ssam struct vba_device *vi; 13932633Ssam struct mpport *mp; 14032633Ssam struct mpevent *ev; 14132633Ssam 14232633Ssam unit = minor(dev); 14332633Ssam mpu = MPUNIT(unit); 14432633Ssam if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0) 14532633Ssam return (ENXIO); 14632633Ssam tp = &mp_tty[unit]; 14732633Ssam if (tp->t_state & TS_XCLUDE && u.u_uid != 0) 14832633Ssam return (EBUSY); 14932633Ssam ms = &mp_softc[mpu]; 15032633Ssam port = MPPORT(unit); 15132633Ssam if (ms->ms_mb->mb_proto[port] != MPPROTO_ASYNC || 15232633Ssam ms->ms_mb->mb_status != MP_OPOPEN) 15332633Ssam return (ENXIO); 15432633Ssam mp = &ms->ms_mb->mb_port[port]; /* host mpcc struct */ 15532633Ssam s = spl8(); 15632633Ssam while (mp->mp_flags & MP_PROGRESS) 15732633Ssam sleep((caddr_t)&tp->t_canq, TTIPRI); 15832633Ssam while (tp->t_state & TS_WOPEN) 15932633Ssam sleep((caddr_t)&tp->t_canq, TTIPRI); 16032633Ssam if (tp->t_state & TS_ISOPEN) { 16132633Ssam splx(s); 16232633Ssam return (0); 16332633Ssam } 16432633Ssam tp->t_state |= TS_WOPEN; 16532633Ssam tp->t_addr = (caddr_t)ms; 16632633Ssam tp->t_oproc = mpstart; 16732633Ssam tp->t_dev = dev; 16832633Ssam ttychars(tp); 16932633Ssam if (tp->t_ispeed == 0) { 17032633Ssam tp->t_ispeed = B9600; 17132633Ssam tp->t_ospeed = B9600; 17232633Ssam tp->t_flags |= ODDP|EVENP|ECHO; 17332633Ssam } 17432633Ssam /* 17532633Ssam * Initialize port state: init MPCC interface 17632633Ssam * structures for port and setup modem control. 17732633Ssam */ 17832633Ssam mp->mp_proto = MPPROTO_ASYNC; /* XXX */ 17932633Ssam error = mpportinit(ms, mp, port); 18032633Ssam if (error) 18132633Ssam goto bad; 18232633Ssam ev = mpparam(unit); 18332633Ssam if (ev == 0) { 18432633Ssam error = ENOBUFS; 18532633Ssam goto bad; 18632633Ssam } 18732633Ssam mpcmd(ev, EVCMD_OPEN, 0, ms->ms_mb, port); 18832633Ssam while ((tp->t_state & TS_CARR_ON) == 0) 18932633Ssam sleep((caddr_t)&tp->t_rawq, TTIPRI); 19032633Ssam error = mpmodem(unit, MMOD_ON); 19132633Ssam if (error) 19232633Ssam goto bad; 19332633Ssam while ((tp->t_state & TS_CARR_ON) == 0) 19432633Ssam sleep((caddr_t)&tp->t_rawq, TTIPRI); 19532633Ssam error = (*linesw[tp->t_line].l_open)(dev,tp); 19632633Ssam done: 19732633Ssam splx(s); 19832633Ssam /* wakeup anyone waiting for open to complete */ 19932633Ssam wakeup((caddr_t)&tp->t_canq); 20032633Ssam 20132633Ssam return (error); 20232633Ssam bad: 20332633Ssam tp->t_state &= ~TS_WOPEN; 20432633Ssam goto done; 20532633Ssam } 20632633Ssam 20732633Ssam /* 20832633Ssam * Close an mpcc port. 20932633Ssam */ 21032633Ssam mpclose(dev) 21132633Ssam dev_t dev; 21232633Ssam { 21332633Ssam register struct tty *tp; 21432633Ssam register struct mpport *mp; 21532633Ssam register struct mpevent *ev; 21632633Ssam int s, port, unit, error; 21732633Ssam struct mblok *mb; 21832633Ssam 21932633Ssam unit = minor(dev); 22032633Ssam tp = &mp_tty[unit]; 22132633Ssam port = MPPORT(unit); 22232633Ssam mb = mp_softc[MPUNIT(unit)].ms_mb; 22332633Ssam mp = &mb->mb_port[port]; 22432633Ssam s = spl8(); 22532633Ssam if (mp->mp_flags & MP_PROGRESS) { /* close in progress */ 22632633Ssam if (mp->mp_flags & MP_REMBSY) { 22732633Ssam mp->mp_flags &= ~MP_REMBSY; 22832633Ssam splx(s); 22932633Ssam return (0); 23032633Ssam } 23132633Ssam while (mp->mp_flags & MP_PROGRESS) 23232633Ssam sleep((caddr_t)&tp->t_canq,TTIPRI); 23332633Ssam } 23432633Ssam error = 0; 23532633Ssam mp->mp_flags |= MP_PROGRESS; 23632633Ssam (*linesw[tp->t_line].l_close)(tp); 23732633Ssam if (tp->t_state & TS_HUPCLS || (tp->t_state & TS_ISOPEN) == 0) 23832633Ssam if (error = mpmodem(unit, MMOD_OFF)) { 23932633Ssam mp->mp_flags &= ~MP_PROGRESS; 24032633Ssam goto out; 24132633Ssam } 24232633Ssam while (tp->t_state & TS_FLUSH) /* ??? */ 24332633Ssam sleep((caddr_t)&tp->t_state, TTOPRI); /* ??? */ 24432633Ssam ttyclose(tp); 24532633Ssam ev = mp_getevent(mp, unit); 24632633Ssam if (ev == 0) { 24732633Ssam error = ENOBUFS; 24832633Ssam goto out; 24932633Ssam } 25032633Ssam mpcmd(ev, EVCMD_CLOSE, 0, mb, port); 25132633Ssam out: 25232633Ssam if (mp->mp_flags & MP_REMBSY) 25332633Ssam mpclean(mb, port); 25432633Ssam splx(s); 25532633Ssam return (error); 25632633Ssam } 25732633Ssam 25832633Ssam /* 25932633Ssam * Read from an mpcc port. 26032633Ssam */ 26132633Ssam mpread(dev, uio) 26232633Ssam dev_t dev; 26332633Ssam struct uio *uio; 26432633Ssam { 26532633Ssam struct tty *tp; 26632633Ssam 26732633Ssam tp = &mp_tty[minor(dev)]; 26832633Ssam return ((*linesw[tp->t_line].l_read)(tp, uio)); 26932633Ssam } 27032633Ssam 27132633Ssam /* 27232633Ssam * Write to an mpcc port. 27332633Ssam */ 27432633Ssam mpwrite(dev, uio) 27532633Ssam dev_t dev; 27632633Ssam struct uio *uio; 27732633Ssam { 27832633Ssam struct tty *tp; 27932633Ssam 28032633Ssam tp = &mp_tty[minor(dev)]; 28132633Ssam return ((*linesw[tp->t_line].l_write)(tp, uio)); 28232633Ssam } 28332633Ssam 28432633Ssam /* 28532633Ssam * Ioctl for a mpcc port 28632633Ssam */ 28732633Ssam mpioctl(dev, cmd, data, flag) 28832633Ssam dev_t dev; 28932633Ssam caddr_t data; 29032633Ssam { 29132633Ssam register struct tty *tp; 29232633Ssam register struct mpsoftc *ms; 29332633Ssam register struct mpevent *ev; 29432633Ssam register struct mpport *mp; 29532633Ssam int s, port, error, unit; 29632633Ssam struct mblok *mb; 29732633Ssam 29832633Ssam unit = minor(dev); 29932633Ssam tp = &mp_tty[unit]; 30032633Ssam ms = &mp_softc[MPUNIT(unit)]; 30132633Ssam mb = ms->ms_mb; 30232633Ssam error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); 30332633Ssam if (error >= 0) 30432633Ssam return (error); 30532633Ssam error = ttioctl(tp, cmd, data, flag); 30632633Ssam if (error >= 0) { 30732633Ssam if (cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCLBIS || 30832633Ssam cmd == TIOCLBIC || cmd == TIOCLSET) { 30932633Ssam ev = mpparam(unit); 31032633Ssam if (ev == 0) 31132633Ssam error = ENOBUFS; 31232633Ssam else 31332633Ssam mpcmd(ev, EVCMD_IOCTL, A_CHGALL, mb, 31432633Ssam MPPORT(unit)); 31532633Ssam } 31632633Ssam return (error); 31732633Ssam } 31832633Ssam switch (cmd) { 31932633Ssam case TIOCSBRK: /* send break */ 32032633Ssam case TIOCCBRK: /* clear break */ 32132633Ssam port = MPPORT(unit); 32232633Ssam mp = &mb->mb_port[port]; 32332633Ssam s = spl8(); 32432633Ssam ev = mp_getevent(mp, unit); 32532633Ssam if (ev) 32632633Ssam mpcmd(ev, EVCMD_IOCTL, 32732633Ssam (cmd == TIOCSBRK ? A_BRKON : A_BRKOFF), 32832633Ssam mb, port); 32932633Ssam else 33032633Ssam error = ENOBUFS; 33132633Ssam splx(s); 33232633Ssam break; 33332633Ssam case TIOCSDTR: /* set dtr control line */ 33432633Ssam break; 33532633Ssam case TIOCCDTR: /* clear dtr control line */ 33632633Ssam break; 33732633Ssam default: 33832633Ssam error = ENOTTY; 33932633Ssam break; 34032633Ssam } 34132633Ssam return (error); 34232633Ssam } 34332633Ssam 34432633Ssam struct mpevent * 34532633Ssam mpparam(unit) 34632633Ssam int unit; 34732633Ssam { 34832633Ssam register struct mpevent *ev; 34932633Ssam register struct mpport *mp; 35032633Ssam register struct tty *tp; 35132633Ssam struct mblok *mb; 35232633Ssam struct mpsoftc *ms; 35332633Ssam register struct asyncparam *asp; 35432633Ssam int port; 35532633Ssam 35632633Ssam ms = &mp_softc[MPUNIT(unit)]; 35732633Ssam mb = ms->ms_mb; 35832633Ssam port = MPPORT(unit); 35932633Ssam mp = &mb->mb_port[port]; 36032633Ssam ev = mp_getevent(mp, unit); /* XXX */ 36132633Ssam if (ev == 0) 36232633Ssam return (ev); 36332633Ssam tp = &mp_tty[unit]; 36432633Ssam /* YUCK */ 36532633Ssam asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1]; 36632633Ssam asp->ap_xon = tp->t_startc; 36732633Ssam asp->ap_xoff = tp->t_stopc; 36832633Ssam asp->ap_xena = 36932633Ssam (tp->t_flags & (RAW|TANDEM)) == TANDEM ? MPA_ENA : MPA_DIS; 37032633Ssam asp->ap_xany = (tp->t_flags & DECCTQ ? MPA_DIS : MPA_ENA); 37132633Ssam #ifdef notnow 37232633Ssam if (tp->t_flags & (RAW|LITOUT|PASS8)) { 37332633Ssam #endif 37432633Ssam asp->ap_data = MPCHAR_8; 37532633Ssam asp->ap_parity = MPPAR_NONE; 37632633Ssam #ifdef notnow 37732633Ssam } else { 37832633Ssam asp->ap_data = MPCHAR_7; 37932633Ssam if ((tp->t_flags & (EVENP|ODDP)) == ODDP) 38032633Ssam asp->ap_parity = MPPAR_ODD; 38132633Ssam else 38232633Ssam asp->ap_parity = MPPAR_EVEN; 38332633Ssam } 38432633Ssam #endif 38532633Ssam if (tp->t_ospeed == B110) 38632633Ssam asp->ap_stop = MPSTOP_2; 38732633Ssam else 38832633Ssam asp->ap_stop = MPSTOP_1; 38932633Ssam if (tp->t_ospeed == EXTA || tp->t_ospeed == EXTB) 39032633Ssam asp->ap_baud = M19200; 39132633Ssam else 39232633Ssam asp->ap_baud = tp->t_ospeed; 39332633Ssam asp->ap_loop = MPA_DIS; /* disable loopback */ 39432633Ssam asp->ap_rtimer = A_RCVTIM; /* default receive timer */ 39532633Ssam if (ms->ms_softCAR & (1<<port)) 39632633Ssam setm(&asp->ap_modem, A_DTR, ASSERT); 39732633Ssam else 39832633Ssam setm(&asp->ap_modem, A_DTR, AUTO); 39932633Ssam seti(&asp->ap_intena, A_DCD); 40032633Ssam return (ev); 40132633Ssam } 40232633Ssam 40332633Ssam mpstart(tp) 40432633Ssam register struct tty *tp; 40532633Ssam { 40632633Ssam register struct mpevent *ev; 40732633Ssam register struct mpport *mp; 40832633Ssam struct mblok *mb; 40932633Ssam struct mpsoftc *ms; 41032633Ssam int port, unit, xcnt, n, s, i; 41132633Ssam struct hxmtl *hxp; 41232633Ssam struct clist outq; 41332633Ssam 41432633Ssam s = spl8(); 41532633Ssam unit = minor(tp->t_dev); 41632633Ssam ms = &mp_softc[MPUNIT(unit)]; 41732633Ssam mb = ms->ms_mb; 41832633Ssam port = MPPORT(unit); 41932633Ssam mp = &mb->mb_port[port]; 42032633Ssam hxp = &ms->ms_hxl[port]; 42132633Ssam xcnt = 0; 42232633Ssam outq = tp->t_outq; 42332633Ssam for (i = 0; i < MPXMIT; i++) { 42432633Ssam if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) 42532633Ssam break; 42632633Ssam if (outq.c_cc <= TTLOWAT(tp)) { 42732633Ssam if (tp->t_state & TS_ASLEEP) { 42832633Ssam tp->t_state &= ~TS_ASLEEP; 42932633Ssam wakeup((caddr_t)&tp->t_outq); 43032633Ssam } 43132633Ssam if (tp->t_wsel) { 43232633Ssam selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); 43332633Ssam tp->t_wsel = 0; 43432633Ssam tp->t_state &= ~TS_WCOLL; 43532633Ssam } 43632633Ssam } 43732633Ssam if (outq.c_cc == 0) 43832633Ssam break; 43932633Ssam /* 44032633Ssam * If we're not currently busy outputting, 44132633Ssam * and there is data to be output, set up 44232633Ssam * port transmit structure to send to mpcc. 44332633Ssam */ 44432633Ssam if (tp->t_flags & (RAW|LITOUT)) 44532633Ssam n = ndqb(&outq, 0); 44632633Ssam else { 44732633Ssam n = ndqb(&outq, 0200); 44832633Ssam if (n == 0) { 44932633Ssam n = getc(&outq); 45032633Ssam timeout(ttrstrt, (caddr_t)tp, (n&0177)+6); 45132633Ssam tp->t_state |= TS_TIMEOUT; 45232633Ssam break; 45332633Ssam } 45432633Ssam } 45532633Ssam hxp->dblock[i] = (caddr_t)vtoph(0, (int)outq.c_cf); 45632633Ssam hxp->size[i] = n; 45732633Ssam xcnt++; /* count of xmts to send */ 45832633Ssam ndadvance(&outq, n); 45932633Ssam } 46032633Ssam /* 46132633Ssam * If data to send, poke mpcc. 46232633Ssam */ 46332633Ssam if (xcnt) { 46432633Ssam ev = mp_getevent(mp, unit); 46532633Ssam if (ev == 0) { 46632633Ssam tp->t_state &= ~(TS_BUSY|TS_TIMEOUT); 46732633Ssam } else { 46832633Ssam tp->t_state |= TS_BUSY; 46932633Ssam ev->ev_count = xcnt; 47032633Ssam mpcmd(ev, EVCMD_WRITE, 0, mb, MPPORT(unit)); 47132633Ssam } 47232633Ssam } 47332633Ssam splx(s); 47432633Ssam } 47532633Ssam 47632633Ssam /* 47732633Ssam * Advance cc bytes from q but don't free memory. 47832633Ssam */ 47932633Ssam ndadvance(q, cc) 48032633Ssam register struct clist *q; 48132633Ssam register cc; 48232633Ssam { 48332633Ssam register struct cblock *bp; 48432633Ssam char *end; 48532633Ssam int rem, s; 48632633Ssam 48732633Ssam s = spltty(); 48832633Ssam if (q->c_cc <= 0) 48932633Ssam goto out; 49032633Ssam while (cc>0 && q->c_cc) { 49132633Ssam bp = (struct cblock *)((int)q->c_cf & ~CROUND); 49232633Ssam if ((int)bp == (((int)q->c_cl-1) & ~CROUND)) { 49332633Ssam end = q->c_cl; 49432633Ssam } else { 49532633Ssam end = (char *)((int)bp + sizeof (struct cblock)); 49632633Ssam } 49732633Ssam rem = end - q->c_cf; 49832633Ssam if (cc >= rem) { 49932633Ssam cc -= rem; 50032633Ssam q->c_cc -= rem; 50132633Ssam q->c_cf = bp->c_next->c_info; 50232633Ssam } else { 50332633Ssam q->c_cc -= cc; 50432633Ssam q->c_cf += cc; 50532633Ssam break; 50632633Ssam } 50732633Ssam } 50832633Ssam if (q->c_cc <= 0) { 50932633Ssam q->c_cf = q->c_cl = NULL; 51032633Ssam q->c_cc = 0; 51132633Ssam } 51232633Ssam out: 51332633Ssam splx(s); 51432633Ssam } 51532633Ssam 51632633Ssam /* 51732633Ssam * Stop output on a line, e.g. for ^S/^Q or output flush. 51832633Ssam */ 51932633Ssam mpstop(tp, rw) 52032633Ssam register struct tty *tp; 52132633Ssam int rw; 52232633Ssam { 52332633Ssam int s, port; 52432633Ssam struct mpevent *ev; 52532633Ssam struct mblok *mb; 52632633Ssam 52732633Ssam s = spl8(); 52832633Ssam /* XXX: DISABLE TRANSMITTER */ 52932633Ssam if (tp->t_state & TS_BUSY) { 53032633Ssam if ((tp->t_state & TS_TTSTOP) == 0) 53132633Ssam tp->t_state |= TS_FLUSH; 53232633Ssam } 53332633Ssam splx(s); 53432633Ssam } 53532633Ssam 53632633Ssam /* 53732633Ssam * Initialize an async port's MPCC state. 53832633Ssam */ 53932633Ssam mpportinit(ms, mp, port) 54032633Ssam register struct mpsoftc *ms; 54132633Ssam register struct mpport *mp; 54232633Ssam int port; 54332633Ssam { 54432633Ssam register struct mpevent *ev; 54532633Ssam register int i; 54632633Ssam caddr_t ptr; 54732633Ssam 54832633Ssam mp->mp_on = mp->mp_off = 0; 54932633Ssam mp->mp_nextrcv = 0; 55032633Ssam mp->mp_flags = 0; 55132633Ssam ev = &mp->mp_recvq[0]; 55232633Ssam for (i = 0; ev < &mp->mp_recvq[MPINSET]; ev++, i++) { 55332633Ssam ev->ev_status = EVSTATUS_FREE; 55432633Ssam ev->ev_cmd = 0; 55532633Ssam ev->ev_opts = 0; 55632633Ssam ev->ev_error = 0; 55732633Ssam ev->ev_flags = 0; 55832633Ssam ev->ev_count = 0; 55932633Ssam ev->ev_un.hxl = (struct hxmtl *) vtoph(0, &ms->ms_hxl[port]); 56032633Ssam ev->ev_params = (caddr_t) vtoph(0, &ms->ms_async[port][i]); 56132633Ssam } 56232633Ssam ev = &mp->mp_sendq[0]; 56332633Ssam for (i = 0; ev < &mp->mp_sendq[MPOUTSET]; ev++, i++) { 56432633Ssam /* init so that L2 can't send any events */ 56532633Ssam /* to host until open has completed */ 56632633Ssam ev->ev_status = EVSTATUS_FREE; 56732633Ssam ev->ev_cmd = 0; 56832633Ssam ev->ev_error = 0; 56932633Ssam ev->ev_flags = 0; 57032633Ssam ev->ev_count = 0; 57132633Ssam ptr = (caddr_t) &ms->ms_cbuf[port][i][0]; 57232633Ssam ev->ev_un.rcvblk = (u_char *)vtoph(0, ptr); 57332633Ssam ev->ev_params = (caddr_t) vtoph(0, ptr); 57432633Ssam } 57532633Ssam return (0); 57632633Ssam } 57732633Ssam 57832633Ssam /* 57932633Ssam * Send an event to an mpcc. 58032633Ssam */ 58132633Ssam mpcmd(ev, cmd, flags, mb, port) 58232633Ssam register struct mpevent *ev; 58332633Ssam struct mblok *mb; 58432633Ssam { 58532633Ssam int s; 58632633Ssam 58732633Ssam s = spl8(); 58832633Ssam /* move host values to inbound entry */ 58932633Ssam ev->ev_cmd = cmd; 59032633Ssam ev->ev_opts = flags; 59132633Ssam /* show event ready for mpcc */ 59232633Ssam ev->ev_status = EVSTATUS_GO; 59332633Ssam mpintmpcc(mb, port); 59432633Ssam splx(s); 59532633Ssam } 59632633Ssam 59732633Ssam /* 59832633Ssam * Return the next available event entry for the indicated port. 59932633Ssam */ 60032633Ssam struct mpevent * 60132633Ssam mp_getevent(mp, unit) 60232633Ssam register struct mpport *mp; 60332633Ssam int unit; 60432633Ssam { 60532633Ssam register struct mpevent *ev; 60632633Ssam int i, s; 60732633Ssam 60832633Ssam s = spl8(); 60932633Ssam ev = &mp->mp_recvq[mp->mp_on]; 61032633Ssam if (ev->ev_status != EVSTATUS_FREE) 61132633Ssam goto bad; 61232633Ssam /* 61332633Ssam * If not a close request, verify one extra 61432633Ssam * event is available for closing the port. 61532633Ssam */ 61632633Ssam if ((mp->mp_flags && MP_PROGRESS) == 0) { 61732633Ssam if ((i = mp->mp_on + 1) >= MPINSET) 61832633Ssam i = 0; 61932633Ssam if (mp->mp_recvq[i].ev_status != EVSTATUS_FREE) 62032633Ssam goto bad; 62132633Ssam } 62232633Ssam /* init inbound fields marking this entry as busy */ 62332633Ssam ev->ev_error = 0; 62432633Ssam ev->ev_flags = 0; 62532633Ssam ev->ev_count = 0; 62632633Ssam ev->ev_status = EVSTATUS_BUSY; 62732633Ssam /* adjust pointer to next available inbound entry */ 62832633Ssam adjptr(mp->mp_on, MPINSET); 62932633Ssam splx(s); 63032633Ssam return (ev); 63132633Ssam bad: 63232633Ssam splx(s); 63332633Ssam log(LOG_ERR, "mp%d: port%d, out of events", MPUNIT(unit), MPPORT(unit)); 63432633Ssam return ((struct mpevent *)0); 63532633Ssam } 63632633Ssam 63732633Ssam mpmodem(unit, flag) 63832633Ssam int unit, flag; 63932633Ssam { 64032633Ssam struct mpsoftc *ms = &mp_softc[MPUNIT(unit)]; 64132633Ssam int port = MPPORT(unit); 64232633Ssam register struct mpport *mp; 64332633Ssam register struct mpevent *ev; 64432633Ssam register struct asyncparam *asp; 64532633Ssam 64632633Ssam mp = &ms->ms_mb->mb_port[port]; 64732633Ssam ev = mp_getevent(mp, unit); 64832633Ssam if (ev == 0) 64932633Ssam return (ENOBUFS); 65032633Ssam /* YUCK */ 65132633Ssam asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1]; 65232633Ssam if (flag == MMOD_ON) { 65332633Ssam if (ms->ms_softCAR & (1 << port)) 65432633Ssam setm(&asp->ap_modem, A_DTR, ASSERT); 65532633Ssam else 65632633Ssam setm(&asp->ap_modem, A_DTR, AUTO); 65732633Ssam seti(&asp->ap_intena, A_DCD); 65832633Ssam } else { 65932633Ssam setm(&asp->ap_modem, 0, DROP); 66032633Ssam seti(&asp->ap_intena, 0); 66132633Ssam } 66232633Ssam mpcmd(ev, EVCMD_IOCTL, A_MDMCHG, ms->ms_mb, port); 66332633Ssam return (0); 66432633Ssam } 66532633Ssam 66632633Ssam /* 66732633Ssam * Set up the modem control structure according to mask. 66832633Ssam * Each set bit in the mask means assert the corresponding 66932633Ssam * modem control line, otherwise, it will be dropped. 67032633Ssam * RTS is special since it can either be asserted, dropped 67132633Ssam * or put in auto mode for auto modem control. 67232633Ssam */ 67332633Ssam static 67432633Ssam setm(mc, mask, rts) 67532633Ssam register struct mdmctl *mc; 67632633Ssam register int mask; 67732633Ssam { 67832633Ssam 67932633Ssam mc->mc_rngdsr = (mask & A_RNGDSR) ? ASSERT : DROP; 68032633Ssam mc->mc_rate = (mask & A_RATE) ? ASSERT : DROP; 68132633Ssam mc->mc_dcd = (mask & A_DCD) ? ASSERT : DROP; 68232633Ssam mc->mc_sectx = (mask & A_SECTX) ? ASSERT : DROP; 68332633Ssam mc->mc_cts = (mask & A_CTS) ? ASSERT : DROP; 68432633Ssam mc->mc_secrx = (mask & A_SECRX) ? ASSERT : DROP; 68532633Ssam mc->mc_dtr = (mask & A_DTR) ? ASSERT : DROP; 68632633Ssam mc->mc_rts = rts; 68732633Ssam } 68832633Ssam 68932633Ssam /* 69032633Ssam * Set up the status change enable field from mask. 69132633Ssam * When a signal is enabled in this structure and 69232633Ssam * and a change in state on a corresponding modem 69332633Ssam * control line occurs, a status change event will 69432633Ssam * be delivered to the host. 69532633Ssam */ 69632633Ssam static 69732633Ssam seti(mc, mask) 69832633Ssam register struct mdmctl *mc; 69932633Ssam register int mask; 70032633Ssam { 70132633Ssam 70232633Ssam mc->mc_rngdsr = (mask & A_RNGDSR) ? MDM_ON : MDM_OFF; 70332633Ssam mc->mc_rate = (mask & A_RATE) ? MDM_ON : MDM_OFF; 70432633Ssam mc->mc_dcd = (mask & A_DCD) ? MDM_ON : MDM_OFF; 70532633Ssam mc->mc_sectx = (mask & A_SECTX) ? MDM_ON : MDM_OFF; 70632633Ssam mc->mc_cts = (mask & A_CTS) ? MDM_ON : MDM_OFF; 70732633Ssam mc->mc_secrx = (mask & A_SECRX) ? MDM_ON : MDM_OFF; 70832633Ssam mc->mc_dtr = (mask & A_DTR) ? MDM_ON : MDM_OFF; 70932633Ssam mc->mc_rts = (mask & A_RTS) ? MDM_ON : MDM_OFF; 71032633Ssam } 71132633Ssam 71232633Ssam mpcleanport(mb, port) 71332633Ssam struct mblok *mb; 71432633Ssam int port; 71532633Ssam { 71632633Ssam register struct mpport *mp; 71732633Ssam register struct tty *tp; 71832633Ssam 71932633Ssam mp = &mb->mb_port[port]; 72032633Ssam if (mp->mp_proto == MPPROTO_ASYNC) { 72132633Ssam mp->mp_flags = MP_REMBSY; 72232633Ssam /* flush I/O queues and send hangup signals */ 72332633Ssam tp = &mp_tty[mb->mb_unit*MPCHUNK+port]; 72432633Ssam tp->t_state &= ~TS_CARR_ON; 72532633Ssam ttyflush(tp, FREAD|FWRITE); 72632633Ssam gsignal(tp->t_pgrp, SIGHUP); 72732633Ssam gsignal(tp->t_pgrp, SIGKILL); 72832633Ssam mpclose(tp->t_dev, 0); 72932633Ssam } 73032633Ssam } 73132633Ssam 73232633Ssam mpclean(mb, port) 73332633Ssam register struct mblok *mb; 73432633Ssam int port; 73532633Ssam { 73632633Ssam register struct mpport *mp; 73732633Ssam register struct mpevent *ev; 73832633Ssam register int i; 73932633Ssam char list[2], *cp; 74032633Ssam int unit; 74132633Ssam 74232633Ssam mp = &mb->mb_port[port]; 74332633Ssam unit = mb->mb_unit; 74432633Ssam for (i = mp->mp_off; i != mp->mp_on; i = (i+1 % MPINSET)) { 74532633Ssam ev = &mp->mp_recvq[i]; 74632633Ssam ev->ev_error = ENXIO; 74732633Ssam ev->ev_status = EVSTATUS_DONE; 74832633Ssam } 74932633Ssam list[0] = port, list[1] = MPPORT_EOL; 75032633Ssam mpxintr(unit, list); 75132633Ssam mprintr(unit, list); 75232633Ssam /* Clear async for port */ 75332633Ssam mp->mp_proto = MPPROTO_UNUSED; 75432633Ssam mp->mp_flags = 0; 75532633Ssam mp->mp_on = 0; 75632633Ssam mp->mp_off = 0; 75732633Ssam mp->mp_nextrcv = 0; 75832633Ssam 75932633Ssam mp_tty[unit*MPCHUNK + port].t_state = 0; 76032633Ssam for (ev = &mp->mp_sendq[0]; ev < &mp->mp_sendq[MPOUTSET]; ev++) { 76132633Ssam ev->ev_status = EVSTATUS_FREE; 76232633Ssam ev->ev_cmd = 0; 76332633Ssam ev->ev_error = 0; 76432633Ssam ev->ev_un.rcvblk = 0; 76532633Ssam ev->ev_params = 0; 76632633Ssam } 76732633Ssam for (ev = &mp->mp_recvq[0]; ev < &mp->mp_recvq[MPINSET]; ev++) { 76832633Ssam ev->ev_status = EVSTATUS_FREE; 76932633Ssam ev->ev_cmd = 0; 77032633Ssam ev->ev_error = 0; 77132633Ssam ev->ev_params = 0; 77232633Ssam } 77332633Ssam } 77432633Ssam 77532633Ssam /* 77632633Ssam * MPCC interrupt handler. 77732633Ssam */ 77832633Ssam mpintr(mpcc) 77932633Ssam int mpcc; 78032633Ssam { 78132633Ssam register struct mblok *mb; 78232633Ssam register struct his *his; 78332633Ssam register int i; 78432633Ssam 78532633Ssam mb = mp_softc[mpcc].ms_mb; 78632633Ssam if (mb == 0) { 78732633Ssam printf("mp%d: stray interrupt\n", mpcc); 78832633Ssam return; 78932633Ssam } 79032633Ssam his = &mb->mb_hostint; 79132633Ssam his->semaphore &= ~MPSEMA_AVAILABLE; 79232633Ssam /* 79332633Ssam * Check for events to be processed. 79432633Ssam */ 79532633Ssam if (his->proto[MPPROTO_ASYNC].outbdone[0] != MPPORT_EOL) 79632633Ssam mprintr(mpcc, his->proto[MPPROTO_ASYNC].outbdone); 79732633Ssam if (his->proto[MPPROTO_ASYNC].inbdone[0] != MPPORT_EOL) 79832633Ssam mpxintr(mpcc, his->proto[MPPROTO_ASYNC].inbdone); 79932633Ssam if (mb->mb_harderr || mb->mb_softerr) 80032633Ssam mperror(mb, mpcc); 80132633Ssam his->semaphore |= MPSEMA_AVAILABLE; 80232633Ssam } 80332633Ssam 80432633Ssam /* 80532633Ssam * Handler for processing completion of transmitted events. 80632633Ssam */ 80732633Ssam mpxintr(unit, list) 80832633Ssam register char *list; 80932633Ssam { 81032633Ssam register struct mpport *mp; 81132633Ssam register struct mpevent *ev; 81232633Ssam register struct mblok *mb; 81332633Ssam register struct tty *tp; 81432633Ssam register struct asyncparam *ap; 81532633Ssam struct mpsoftc *ms; 81632633Ssam int port, i, j; 81732633Ssam 81832633Ssam ms = &mp_softc[unit]; 81932633Ssam mb = mp_softc[unit].ms_mb; 82032633Ssam for (j = 0; j < MPMAXPORT && ((port = *list++) != MPPORT_EOL); j++) { 82132633Ssam /* 82232633Ssam * Process each completed entry in the inbound queue. 82332633Ssam */ 82432633Ssam mp = &mb->mb_port[port]; 82532633Ssam tp = &mp_tty[unit*MPCHUNK + port]; 82632633Ssam #define nextevent(mp) &mp->mp_recvq[mp->mp_off] 82732633Ssam ev = nextevent(mp); 82832633Ssam for(; ev->ev_status & EVSTATUS_DONE; ev = nextevent(mp)) { 82932633Ssam /* YUCK */ 83032633Ssam ap = &ms->ms_async[port][mp->mp_off]; 83132633Ssam mppurge(ap, sizeof (*ap)); 83232633Ssam switch (ev->ev_cmd) { 83332633Ssam case EVCMD_OPEN: 83432633Ssam /* 83532633Ssam * Open completion, start all reads and 83632633Ssam * assert modem status information. 83732633Ssam */ 83832633Ssam for (i = 0; i < MPOUTSET; i++) 83932633Ssam mp->mp_sendq[i].ev_status = EVSTATUS_GO; 84032633Ssam (*linesw[tp->t_line].l_modem) 84132633Ssam (tp, ap->ap_modem.mc_dcd == ASSERT); 84232633Ssam break; 84332633Ssam case EVCMD_CLOSE: 84432633Ssam /* 84532633Ssam * Close completion, flush all pending 84632633Ssam * transmissions, free resources, and 84732633Ssam * cleanup mpcc port state. 84832633Ssam */ 84932633Ssam for (i = 0; i < MPOUTSET; i++) { 85032633Ssam mp->mp_sendq[i].ev_status = 85132633Ssam EVSTATUS_FREE; 85232633Ssam mp->mp_sendq[i].ev_un.rcvblk = 0; 85332633Ssam mp->mp_sendq[i].ev_params = 0; 85432633Ssam } 85532633Ssam tp->t_state &= ~TS_CARR_ON; 85632633Ssam mp->mp_on = mp->mp_off = mp->mp_nextrcv = 0; 85732633Ssam mp->mp_flags &= ~MP_PROGRESS; 85832633Ssam mp->mp_proto = MPPROTO_UNUSED; 85932633Ssam wakeup((caddr_t)&tp->t_canq); /* ??? */ 86032633Ssam goto done; 86132633Ssam case EVCMD_IOCTL: 86232633Ssam /* 86332633Ssam * Nothing to do, just pitch. 86432633Ssam */ 86532633Ssam break; 86632633Ssam case EVCMD_WRITE: 86732633Ssam /* 86832633Ssam * Transmission completed, update tty 86932633Ssam * state and restart output. 87032633Ssam */ 87132633Ssam tp->t_state &= ~TS_BUSY; 87232633Ssam if (tp->t_state & TS_FLUSH) { 87332633Ssam tp->t_state &= ~TS_FLUSH; 87432633Ssam wakeup((caddr_t)&tp->t_state); 87532633Ssam } else { 87632633Ssam register int cc = 0, i; 87732633Ssam struct hxmtl *hxp; 87832633Ssam 87932633Ssam hxp = &ms->ms_hxl[port]; 88032633Ssam for(i = 0; i < ev->ev_count; i++) 88132633Ssam cc += hxp->size[i]; 88232633Ssam ndflush(&tp->t_outq, cc); 88332633Ssam } 88432633Ssam switch (ev->ev_error) { 88532633Ssam case A_SIZERR: /*# error in xmt data size */ 88632633Ssam mplog(unit, port, A_XSIZE, 0); 88732633Ssam break; 88832633Ssam case A_NXBERR: /*# no more xmt evt buffers */ 88932633Ssam mplog(unit, port, A_NOXBUF, 0); 89032633Ssam break; 89132633Ssam } 89232633Ssam mpstart(tp); 89332633Ssam break; 89432633Ssam default: 89532633Ssam mplog(unit, port, A_INVCMD, ev->ev_cmd); 89632633Ssam break; 89732633Ssam } 89832633Ssam /* re-init all values in this entry */ 89932633Ssam ev->ev_cmd = 0; 90032633Ssam ev->ev_opts = 0; 90132633Ssam ev->ev_error = 0; 90232633Ssam ev->ev_flags = 0; 90332633Ssam ev->ev_count = 0; 90432633Ssam /* show this entry is available for use */ 90532633Ssam ev->ev_status = EVSTATUS_FREE; 90632633Ssam adjptr(mp->mp_off, MPINSET); 90732633Ssam #undef nextevent 90832633Ssam } 90932633Ssam done: 91032633Ssam ; 91132633Ssam } 91232633Ssam } 91332633Ssam 91432633Ssam /* 91532633Ssam * Handler for processing received events. 91632633Ssam */ 91732633Ssam mprintr(unit, list) 91832633Ssam char *list; 91932633Ssam { 92032633Ssam register struct tty *tp; 92132633Ssam register struct mpport *mp; 92232633Ssam register struct mpevent *ev; 92332633Ssam struct mblok *mb; 92432633Ssam register int cc; 92532633Ssam register char *cp; 92632633Ssam struct mpsoftc *ms; 92732633Ssam caddr_t ptr; 92832633Ssam char *rcverr; 92932633Ssam int port, i; 93032633Ssam 93132633Ssam ms = &mp_softc[unit]; 93232633Ssam mb = mp_softc[unit].ms_mb; 93332633Ssam for (i = 0; i < MPMAXPORT && (port = *list++) != MPPORT_EOL; i++) { 93432633Ssam tp = &mp_tty[unit*MPCHUNK + port]; 93532633Ssam mp = &mb->mb_port[port]; 93632633Ssam ev = &mp->mp_sendq[mp->mp_nextrcv]; 93732633Ssam while (ev->ev_status & EVSTATUS_DONE) { 93832633Ssam if (ev->ev_cmd != EVCMD_READ && 93932633Ssam ev->ev_cmd != EVCMD_STATUS) { 94032633Ssam mplog(unit, port, "unexpected command", 94132633Ssam ev->ev_cmd); 94232633Ssam goto next; 94332633Ssam } 94432633Ssam if (ev->ev_cmd == EVCMD_STATUS) { 94532633Ssam /* 94632633Ssam * Status change, look for carrier changes. 94732633Ssam */ 94832633Ssam if (ev->ev_opts == DCDASRT || 94932633Ssam ev->ev_opts == DCDDROP) 95032633Ssam (*linesw[tp->t_line].l_modem) 95132633Ssam (tp, ev->ev_opts == DCDASRT); 95232633Ssam else 95332633Ssam mplog(unit, port, 95432633Ssam "unexpect status command", 95532633Ssam ev->ev_opts); 95632633Ssam goto next; 95732633Ssam } 95832633Ssam /* 95932633Ssam * Process received data. 96032633Ssam */ 96132633Ssam if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) 96232633Ssam goto next; 96332633Ssam cc = ev->ev_count; 96432633Ssam if (cc == 0) 96532633Ssam goto next; 96632633Ssam /* YUCK */ 96732633Ssam cp = ms->ms_cbuf[port][mp->mp_nextrcv]; 96832633Ssam mppurge(cp, CBSIZE); 96932633Ssam while (cc-- > 0) { 97032633Ssam /* 97132633Ssam * A null character is inserted, potentially 97232633Ssam * when a break or framing error occurs. If 97332633Ssam * we're not in raw mode, substitute the 97432633Ssam * interrupt character. 97532633Ssam */ 97632633Ssam if (*cp == 0 && 97732633Ssam (ev->ev_error == BRKASRT || 97832633Ssam ev->ev_error == FRAMERR)) 97932633Ssam if ((tp->t_flags&RAW) == 0) 98032633Ssam *cp = tp->t_intrc; 98132633Ssam (*linesw[tp->t_line].l_rint)(*cp++, tp); 98232633Ssam } 98332633Ssam /* setup for next read */ 98432633Ssam ptr = (caddr_t)&mp_softc[unit].ms_cbuf[port][mp->mp_nextrcv][0]; 98532633Ssam ev->ev_un.rcvblk = (u_char *)vtoph(0, ptr); 98632633Ssam ev->ev_params = (caddr_t) vtoph(0, ptr); 98732633Ssam switch(ev->ev_error) { 98832633Ssam case RCVDTA: /* Normal (good) rcv data */ 98932633Ssam rcverr = (char *)0; 99032633Ssam break; 99132633Ssam case PARERR: /* parity error */ 99232633Ssam rcverr = "parity error"; 99332633Ssam break; 99432633Ssam case FRAMERR: /* frame error */ 99532633Ssam rcverr = "frame error"; 99632633Ssam break; 99732633Ssam case OVRNERR: /* Overrun error */ 99832633Ssam rcverr = "overrun error"; 99932633Ssam break; 100032633Ssam case OVFERR: /* Overflow error */ 100132633Ssam rcverr = "overflow error"; 100232633Ssam break; 100332633Ssam default: 100432633Ssam rcverr = "undefined rcv error"; 100532633Ssam } 100632633Ssam if (rcverr != (char *)0) 100732633Ssam mplog(unit, port, rcverr, ev->ev_error); 100832633Ssam next: 100932633Ssam ev->ev_cmd = 0; 101032633Ssam ev->ev_opts = 0; 101132633Ssam ev->ev_error = 0; 101232633Ssam ev->ev_flags = 0; 101332633Ssam ev->ev_status = EVSTATUS_GO; /* start next read */ 101432633Ssam adjptr(mp->mp_nextrcv, MPOUTSET); 101532633Ssam ev = &mp->mp_sendq[mp->mp_nextrcv]; 101632633Ssam } 101732633Ssam } 101832633Ssam } 101932633Ssam 102032633Ssam /* 102132633Ssam * Log an mpcc diagnostic. 102232633Ssam */ 102332633Ssam mplog(unit, port, cp, flags) 102432633Ssam char *cp; 102532633Ssam { 102632633Ssam 102732633Ssam if (flags) 102832633Ssam log(LOG_ERR, "mp%d: port%d, %s (%d)\n", 102932633Ssam unit, port, cp, flags); 103032633Ssam else 103132633Ssam log(LOG_ERR, "mp%d: port%d, %s\n", unit, port, cp); 103232633Ssam } 103332633Ssam 103432633Ssam int MPHOSTINT = 1; 103532633Ssam 103632633Ssam mptimeint(mb) 103732633Ssam register struct mblok *mb; 103832633Ssam { 103932633Ssam 104032633Ssam mb->mb_mpintcnt = 0; 104132633Ssam mb->mb_mpintclk = (caddr_t)0; 104232633Ssam *(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2; 104332633Ssam } 104432633Ssam 104532633Ssam /* 104632633Ssam * Interupt mpcc 104732633Ssam */ 104832633Ssam mpintmpcc(mb, port) 104932633Ssam register struct mblok *mb; 105032633Ssam u_short port; 105132633Ssam { 105232633Ssam 105332633Ssam mb->mb_intr[port] |= MPSEMA_WORK; 105432633Ssam if (++mb->mb_mpintcnt == MPHOSTINT) { 105532633Ssam mb->mb_mpintcnt = 0; 105632633Ssam *(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2; 105732633Ssam if (mb->mb_mpintclk) { 105832633Ssam untimeout(mptimeint, mb); 105932633Ssam mb->mb_mpintclk = 0; 106032633Ssam } 106132633Ssam } else { 106232633Ssam if (mb->mb_mpintclk == 0) { 106332633Ssam timeout(mptimeint, mb, 4); 106432633Ssam mb->mb_mpintclk = (caddr_t)1; 106532633Ssam } 106632633Ssam } 106732633Ssam } 106832633Ssam 106932633Ssam static char *mpherrmsg[] = { 107032633Ssam "", 107132633Ssam "Bus error", /* MPBUSERR */ 107232633Ssam "Address error", /* ADDRERR */ 107332633Ssam "Undefined ecc interrupt", /* UNDECC */ 107432633Ssam "Undefined interrupt", /* UNDINT */ 107532633Ssam "Power failure occurred", /* PWRFL */ 107632633Ssam "Stray transmit done interrupt", /* NOXENTRY */ 107732633Ssam "Two fast timers on one port", /* TWOFTMRS */ 107832633Ssam "Interrupt queue full", /* INTQFULL */ 107932633Ssam "Interrupt queue ack error", /* INTQERR */ 108032633Ssam "Uncorrectable dma parity error", /* CBPERR */ 108132633Ssam "32 port ACAP failed power up", /* ACPDEAD */ 108232633Ssam }; 108332633Ssam #define NHERRS (sizeof (mpherrmsg) / sizeof (mpherrmsg[0])) 108432633Ssam 108532633Ssam mperror(mb, unit) 108632633Ssam register struct mblok *mb; 108732633Ssam int unit; 108832633Ssam { 108932633Ssam register char *cp; 109032633Ssam register int i; 109132633Ssam 109232633Ssam if (mb->mb_softerr) { 109332633Ssam switch (mb->mb_softerr) { 109432633Ssam case DMAPERR: /* dma parity error */ 109532633Ssam cp = "dma parity error"; 109632633Ssam break; 109732633Ssam case ECCERR: 109832633Ssam cp = "local memory ecc error"; 109932633Ssam break; 110032633Ssam default: 110132633Ssam cp = "unknown error"; 110232633Ssam break; 110332633Ssam } 110432633Ssam log(LOG_ERR, "mp%d: soft error, %s", unit, cp); 110532633Ssam mb->mb_softerr = 0; 110632633Ssam } 110732633Ssam if (mb->mb_harderr) { 110832633Ssam if (mb->mb_harderr < NHERRS) 110932633Ssam cp = mpherrmsg[mb->mb_harderr]; 111032633Ssam else 111132633Ssam cp = "unknown error"; 111232633Ssam log(LOG_ERR, "mp%d: hard error, %s", unit, cp); 111332633Ssam if (mb->mb_status == MP_OPOPEN) { 111432633Ssam for (i = 0; i < MPMAXPORT; i++) { 111532633Ssam mpcleanport(mb, i); 111632633Ssam mb->mb_proto[i] = MPPROTO_UNUSED; 111732633Ssam } 111832633Ssam } 111932633Ssam mb->mb_harderr = 0; 112032633Ssam mb->mb_status = 0; 112132633Ssam } 112232633Ssam } 112332633Ssam 112432633Ssam mppurge(addr, cc) 112532633Ssam register caddr_t addr; 112632633Ssam register int cc; 112732633Ssam { 112832633Ssam 112932633Ssam for (; cc >= 0; addr += NBPG, cc -= NBPG) 113032633Ssam mtpr(P1DC, addr); 113132633Ssam } 113232633Ssam 113332633Ssam /* 113432633Ssam * MPCC Download Pseudo-device. 113532633Ssam */ 113632633Ssam char mpdlbuf[MPDLBUFSIZE]; 113732633Ssam int mpdlbusy; /* interlock on download buffer */ 113832633Ssam int mpdlerr; 113932633Ssam 114032633Ssam mpdlopen(dev) 114132633Ssam dev_t dev; 114232633Ssam { 114332633Ssam int unit, mpu; 114432633Ssam struct vba_device *vi; 114532633Ssam 114632633Ssam unit = minor(dev); 114732633Ssam mpu = MPUNIT(unit); 114832633Ssam if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0) 114932633Ssam return (ENODEV); 115032633Ssam return (0); 115132633Ssam } 115232633Ssam 115332633Ssam mpdlwrite(dev, uio) 115432633Ssam dev_t dev; 115532633Ssam struct uio *uio; 115632633Ssam { 115732633Ssam register struct mpsoftc *ms = &mp_softc[MPUNIT(minor(dev))]; 115832633Ssam register struct mpdl *dl; 115932633Ssam int error; 116032633Ssam 116132633Ssam if (ms->ms_mb == 0 || ms->ms_mb->mb_status != MP_DLOPEN) 116232633Ssam return (EFAULT); 116332633Ssam dl = &ms->ms_mb->mb_dl; 116432633Ssam dl->mpdl_count = uio->uio_iov->iov_len; 116532633Ssam dl->mpdl_data = (caddr_t) vtoph((struct proc *)0, mpdlbuf); 116632633Ssam if (error = uiomove(mpdlbuf, dl->mpdl_count, UIO_WRITE, uio)) 116732633Ssam return (error); 116832633Ssam uio->uio_resid -= dl->mpdl_count; /* set up return from write */ 116932633Ssam dl->mpdl_cmd = MPDLCMD_NORMAL; 117032633Ssam error = mpdlwait(dl); 117132633Ssam return (error); 117232633Ssam } 117332633Ssam 117432633Ssam mpdlclose(dev) 117532633Ssam dev_t dev; 117632633Ssam { 117732633Ssam register struct mblok *mb = mp_softc[MPUNIT(minor(dev))].ms_mb; 117832633Ssam int ret = 0; 117932633Ssam 118032633Ssam if (mb == 0 || mb->mb_status != MP_DLDONE) { 118132633Ssam mpbogus.status = 0; 118232633Ssam if (mpbogus.mb == mpbogus.mbloks[MPUNIT(minor(dev))]) 118332633Ssam mpdlbusy--; 118432633Ssam return (EEXIST); 118532633Ssam } 118632633Ssam mb->mb_status = MP_OPOPEN; 118732633Ssam mpbogus.status = 0; 118832633Ssam /* set to dead, for board handshake */ 118932633Ssam mb->mb_hostint.imok = MPIMOK_DEAD; 119032633Ssam return (0); 119132633Ssam } 119232633Ssam 1193*32681Skarels mpreset(dev) 1194*32681Skarels dev_t dev; 1195*32681Skarels { 1196*32681Skarels /* XXX */ 1197*32681Skarels } 1198*32681Skarels 119932633Ssam int mpdltimeout(); 120032633Ssam 120132633Ssam mpdlioctl(dev, cmd, data, flag) 120232633Ssam dev_t dev; 120332633Ssam caddr_t data; 120432633Ssam { 120532633Ssam register struct mblok *mb; 120632633Ssam register struct mpdl *dl; 120732633Ssam int unit, error, s, i, j; 120832633Ssam 120932633Ssam mb = mp_softc[unit=MPUNIT(minor(dev))].ms_mb; 121032633Ssam if (mb == 0) 121132633Ssam return (EEXIST); 121232633Ssam dl = &mb->mb_dl; 121332633Ssam error = 0; 121432633Ssam switch (cmd) { 121532633Ssam case MPIOPORTMAP: 121632633Ssam bcopy(data, (caddr_t)mb->mb_proto, sizeof (mb->mb_proto)); 121732633Ssam break; 121832633Ssam case MPIOHILO: 121932633Ssam bcopy(data, (caddr_t)&mb->mb_hiport, 2*(sizeof(mb->mb_hiport))); 122032633Ssam break; 122132633Ssam case MPIOENDDL: 122232633Ssam dl->mpdl_count = 0; 122332633Ssam dl->mpdl_data = 0; 122432633Ssam dl->mpdl_cmd = MPIOENDDL&IOCPARM_MASK; 122532633Ssam error = mpdlwait(dl); 122632633Ssam mpccinit(unit); 122732633Ssam mb->mb_status = MP_DLDONE; 122832633Ssam mpdlbusy--; 122932633Ssam break; 123032633Ssam case MPIOENDCODE: 123132633Ssam dl->mpdl_count = 0; 123232633Ssam dl->mpdl_data = 0; 123332633Ssam dl->mpdl_cmd = MPIOENDCODE&IOCPARM_MASK; 123432633Ssam error = mpdlwait(dl); 123532633Ssam break; 123632633Ssam case MPIOASYNCNF: 123732633Ssam bcopy(data, mpdlbuf, sizeof (struct abdcf)); 123832633Ssam dl->mpdl_data = (caddr_t) vtoph((struct proc *)0, mpdlbuf); 123932633Ssam dl->mpdl_count = sizeof (struct abdcf); 124032633Ssam dl->mpdl_cmd = MPIOASYNCNF&IOCPARM_MASK; 124132633Ssam error = mpdlwait(dl); 124232633Ssam break; 124332633Ssam case MPIOSTARTDL: 124432633Ssam while (mpdlbusy) 124532633Ssam sleep((caddr_t)&mpdlbusy, PZERO+1); 124632633Ssam mpdlbusy++; 124732633Ssam /* initialize the downloading interface */ 124832633Ssam mpbogus.magic = MPMAGIC; 124932633Ssam mpbogus.mb = mpbogus.mbloks[unit]; 125032633Ssam mpbogus.status = 1; 125132633Ssam dl->mpdl_status = EVSTATUS_FREE; 125232633Ssam dl->mpdl_count = 0; 125332633Ssam dl->mpdl_cmd = 0; 125432633Ssam dl->mpdl_data = (char *) 0; 125532633Ssam mpdlerr = 0; 125632633Ssam mb->mb_magic = MPMAGIC; 125732633Ssam mb->mb_ivec = mp_softc[unit].ms_ivec+1; /* download vector */ 125832633Ssam mb->mb_status = MP_DLPEND; 125932633Ssam mb->mb_diagswitch[0] = 'A'; 126032633Ssam mb->mb_diagswitch[1] = 'P'; 126132633Ssam s = spl8(); 126232633Ssam *(u_short *)mpinfo[unit]->ui_addr = 2; 126332633Ssam timeout(mpdltimeout, mb, 30*hz); /* approx 15 seconds */ 126432633Ssam sleep((caddr_t)&mb->mb_status, PZERO+1); 126532633Ssam splx(s); 126632633Ssam if (mb->mb_status == MP_DLOPEN) { 126732633Ssam untimeout(mpdltimeout, mb); 126832633Ssam } else if (mb->mb_status == MP_DLTIME) { 126932633Ssam mpbogus.status = 0; 127032633Ssam error = ETIMEDOUT; 127132633Ssam } else { 127232633Ssam mpbogus.status = 0; 127332633Ssam error = ENXIO; 127432633Ssam log(LOG_ERR, "mp%d: start download: unknown status %x", 127532633Ssam unit, mb->mb_status); 127632633Ssam } 127732633Ssam bzero(mb->mb_port, sizeof (mb->mb_port)); 127832633Ssam break; 127932633Ssam case MPIORESETBOARD: 128032633Ssam s = spl8(); 128132633Ssam if (mb->mb_imokclk) 128232633Ssam mb->mb_imokclk = 0; 128332633Ssam *(u_short *)mpinfo[unit]->ui_addr = 0x100; 128432633Ssam if (mb->mb_status == MP_DLOPEN || mb->mb_status == MP_DLDONE) { 128532633Ssam mpdlerr = MP_DLERROR; 128632633Ssam dl->mpdl_status = EVSTATUS_FREE; 128732633Ssam wakeup((caddr_t)&dl->mpdl_status); 128832633Ssam mpbogus.status = 0; 128932633Ssam } 129032633Ssam for (i = 0; i < MPMAXPORT; i++) { 129132633Ssam if (mb->mb_harderr || mb->mb_softerr) 129232633Ssam mperror(mb, i); 129332633Ssam mpcleanport(mb, i); 129432633Ssam mb->mb_proto[i] = MPPROTO_UNUSED; 129532633Ssam } 129632633Ssam mb->mb_status = 0; 129732633Ssam splx(s); 129832633Ssam break; 129932633Ssam default: 130032633Ssam error = EINVAL; 130132633Ssam break; 130232633Ssam } 130332633Ssam return (error); 130432633Ssam } 130532633Ssam 130632633Ssam mpccinit(unit) 130732633Ssam int unit; 130832633Ssam { 130932633Ssam register struct mblok *mb = mp_softc[unit].ms_mb; 131032633Ssam register struct his *his; 131132633Ssam register int i, j; 131232633Ssam 131332633Ssam mb->mb_status = MP_DLDONE; 131432633Ssam mb->mb_ivec = mp_softc[unit].ms_ivec; 131532633Ssam mb->mb_magic = MPMAGIC; 131632633Ssam /* Init host interface structure */ 131732633Ssam his = &mb->mb_hostint; 131832633Ssam his->semaphore = MPSEMA_AVAILABLE; 131932633Ssam for (i = 0; i < NMPPROTO; i++) 132032633Ssam for (j = 0; j < MPMAXPORT; j++) { 132132633Ssam his->proto[i].inbdone[j] = MPPORT_EOL; 132232633Ssam his->proto[i].outbdone[j] = MPPORT_EOL; 132332633Ssam } 132432633Ssam mb->mb_unit = unit; 132532633Ssam } 132632633Ssam 132732633Ssam mpdlintr(mpcc) 132832633Ssam int mpcc; 132932633Ssam { 133032633Ssam register struct mblok *mb; 133132633Ssam register struct mpdl *dl; 133232633Ssam 133332633Ssam mb = mp_softc[mpcc].ms_mb; 133432633Ssam if (mb == 0) { 133532633Ssam printf("mp%d: stray download interrupt\n", mpcc); 133632633Ssam return; 133732633Ssam } 133832633Ssam dl = &mb->mb_dl; 133932633Ssam switch (mb->mb_status) { 134032633Ssam case MP_DLOPEN: 134132633Ssam if (dl->mpdl_status != EVSTATUS_DONE) 134232633Ssam mpdlerr = MP_DLERROR; 134332633Ssam dl->mpdl_status = EVSTATUS_FREE; 134432633Ssam wakeup((caddr_t)&dl->mpdl_status); 134532633Ssam return; 134632633Ssam case MP_DLPEND: 134732633Ssam mb->mb_status = MP_DLOPEN; 134832633Ssam wakeup(&mb->mb_status); 134932633Ssam /* fall thru... */ 135032633Ssam case MP_DLTIME: 135132633Ssam return; 135232633Ssam case MP_OPOPEN: 135332633Ssam if (mb->mb_imokclk) 135432633Ssam mb->mb_imokclk = 0; 135532633Ssam mb->mb_nointcnt = 0; /* reset no interrupt count */ 135632633Ssam mb->mb_hostint.imok = MPIMOK_DEAD; 135732633Ssam mb->mb_imokclk = (caddr_t)1; 135832633Ssam break; 135932633Ssam default: 136032633Ssam log(LOG_ERR, "mp%d: mpdlintr, status %x\n", 136132633Ssam mpcc, mb->mb_status); 136232633Ssam break; 136332633Ssam } 136432633Ssam } 136532633Ssam 136632633Ssam mpdltimeout(mp) 136732633Ssam struct mblok *mp; 136832633Ssam { 136932633Ssam 137032633Ssam mp->mb_status = MP_DLTIME; 137132633Ssam wakeup((caddr_t)&mp->mb_status); 137232633Ssam } 137332633Ssam 137432633Ssam /* 137532633Ssam * Wait for a transfer to complete or a timeout to occur. 137632633Ssam */ 137732633Ssam mpdlwait(dl) 137832633Ssam register struct mpdl *dl; 137932633Ssam { 138032633Ssam int s, error = 0; 138132633Ssam 138232633Ssam s = spl8(); 138332633Ssam dl->mpdl_status = EVSTATUS_GO; 138432633Ssam while (dl->mpdl_status != EVSTATUS_FREE) { 138532633Ssam sleep((caddr_t)&dl->mpdl_status, PZERO+1); 138632633Ssam if (mpdlerr == MP_DLERROR) 138732633Ssam error = EIO; 138832633Ssam } 138932633Ssam splx(s); 139032633Ssam return (error); 139132633Ssam } 139232633Ssam #endif 1393