Annotation of Net2/arch/tahoe/vba/mp.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1988 Regents of the University of California.
        !             3:  * All rights reserved.
        !             4:  *
        !             5:  * This code is derived from software contributed to Berkeley by
        !             6:  * Computer Consoles Inc.
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            11:  * 1. Redistributions of source code must retain the above copyright
        !            12:  *    notice, this list of conditions and the following disclaimer.
        !            13:  * 2. Redistributions in binary form must reproduce the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer in the
        !            15:  *    documentation and/or other materials provided with the distribution.
        !            16:  * 3. All advertising materials mentioning features or use of this software
        !            17:  *    must display the following acknowledgement:
        !            18:  *     This product includes software developed by the University of
        !            19:  *     California, Berkeley and its contributors.
        !            20:  * 4. Neither the name of the University nor the names of its contributors
        !            21:  *    may be used to endorse or promote products derived from this software
        !            22:  *    without specific prior written permission.
        !            23:  *
        !            24:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            25:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            26:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            27:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            28:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            29:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            30:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            31:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            32:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            33:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            34:  * SUCH DAMAGE.
        !            35:  *
        !            36:  *     @(#)mp.c        7.17 (Berkeley) 5/16/91
        !            37:  */
        !            38: 
        !            39: #include "mp.h"
        !            40: #if NMP > 0
        !            41: /*
        !            42:  * Multi Protocol Communications Controller (MPCC).
        !            43:  * Asynchronous Terminal Protocol Support.
        !            44:  */
        !            45: #include "sys/param.h"
        !            46: #include "sys/ioctl.h"
        !            47: #include "sys/tty.h"
        !            48: #include "sys/user.h"
        !            49: #include "sys/map.h"
        !            50: #include "sys/buf.h"
        !            51: #include "sys/conf.h"
        !            52: #include "sys/file.h"
        !            53: #include "sys/errno.h"
        !            54: #include "sys/syslog.h"
        !            55: #include "sys/vmmac.h"
        !            56: #include "sys/kernel.h"
        !            57: #include "sys/clist.h"
        !            58: 
        !            59: #include "../include/pte.h"
        !            60: #include "../include/mtpr.h"
        !            61: 
        !            62: #include "../vba/vbavar.h"
        !            63: #include "../vba/mpreg.h"
        !            64: 
        !            65: #define        MPCHUNK 16
        !            66: #define        MPPORT(n)       ((n) & 0xf)
        !            67: #define        MPUNIT(n)       ((n) >> 4)
        !            68: 
        !            69: /*
        !            70:  * Driver information for auto-configuration stuff.
        !            71:  */
        !            72: int     mpprobe(), mpattach(), mpintr();
        !            73: struct  vba_device *mpinfo[NMP];
        !            74: long    mpstd[] = { 0 };
        !            75: struct  vba_driver mpdriver =
        !            76:     { mpprobe, 0, mpattach, 0, mpstd, "mp", mpinfo };
        !            77: 
        !            78: int    mpstart();
        !            79: int    mpparam();
        !            80: struct mpevent *mpparam2();
        !            81: struct mpevent *mp_getevent();
        !            82: 
        !            83: /*
        !            84:  * The following structure is needed to deal with mpcc's convoluted
        !            85:  * method for locating it's mblok structures (hold your stomach).
        !            86:  * When an mpcc is reset at boot time it searches host memory
        !            87:  * looking for a string that says ``ThIs Is MpCc''.  The mpcc
        !            88:  * then reads the structure to locate the pointer to it's mblok
        !            89:  * structure (you can wretch now).
        !            90:  */
        !            91: struct mpbogus {
        !            92:        char    s[12];                  /* `ThIs Is MpCc'' */
        !            93:        u_char  status;
        !            94:        u_char  unused;
        !            95:        u_short magic;
        !            96:        struct  mblok *mb;
        !            97:        struct  mblok *mbloks[NMP];     /* can support at most 16 mpcc's */
        !            98: } mpbogus = { 'T','h','I','s',' ','I','s',' ','M','p','C','c' };
        !            99: 
        !           100: /*
        !           101:  * Software state per unit.
        !           102:  */
        !           103: struct mpsoftc {
        !           104:        u_int   ms_ivec;                /* interrupt vector */
        !           105:        u_int   ms_softCAR;             /* software carrier for async */
        !           106:        struct  mblok *ms_mb;           /* mpcc status area */
        !           107:        struct  vb_buf ms_buf;          /* vba resources for ms_mb */
        !           108:        struct  hxmtl ms_hxl[MPMAXPORT];/* host transmit list */
        !           109:        struct  asyncparam ms_async[MPMAXPORT][MPINSET];/* async structs */
        !           110:        char    ms_cbuf[MPMAXPORT][MPOUTSET][CBSIZE];/* input character buffers */
        !           111: } mp_softc[NMP];
        !           112: 
        !           113: struct speedtab          
        !           114: mpspeedtab[] = {
        !           115:        9600,   M9600,    /* baud rate = 9600 */
        !           116:        4800,   M4800,    /* baud rate = 4800 */
        !           117:        2400,   M2400,    /* baud rate = 2400 */
        !           118:        1800,   M1800,    /* baud rate = 1800 */
        !           119:        1200,   M1200,    /* baud rate = 1200 */
        !           120:        600,    M600,     /* baud rate = 600 */
        !           121:        300,    M300,     /* baud rate = 300 */
        !           122:        200,    M200,     /* baud rate = 200 */
        !           123:        150,    M150,     /* baud rate = 150 */
        !           124:        134,    M134_5,   /* baud rate = 134.5 */
        !           125:        110,    M110,     /* baud rate = 110 */
        !           126:        75,     M75,      /* baud rate = 75 */
        !           127:        50,     M50,      /* baud rate = 50 */
        !           128:        0,      M0,       /* baud rate = 0 */
        !           129:        2000,   M2000,    /* baud rate = 2000 */
        !           130:        3600,   M3600,    /* baud rate = 3600 */
        !           131:        7200,   M7200,    /* baud rate = 7200 */
        !           132:        19200,  M19200,   /* baud rate = 19,200 */
        !           133:        24000,  M24000,   /* baud rate = 24,000 */
        !           134:        28400,  M28400,   /* baud rate = 28,400 */
        !           135:        37800,  M37800,   /* baud rate = 37,800 */
        !           136:        40300,  M40300,   /* baud rate = 40,300 */
        !           137:        48000,  M48000,   /* baud rate = 48,000 */
        !           138:        52000,  M52000,   /* baud rate = 52,000 */
        !           139:        56800,  M56800,   /* baud rate = 56,800 */
        !           140:        EXTA,   MEXTA,    /* baud rate = Ext A */
        !           141:        EXTB,   MEXTB,    /* baud rate = Ext B */
        !           142:        -1,     -1,
        !           143: };
        !           144: 
        !           145: struct tty mp_tty[NMP*MPCHUNK];
        !           146: #ifndef lint
        !           147: int    nmp = NMP*MPCHUNK;
        !           148: #endif
        !           149: 
        !           150: int    ttrstrt();
        !           151: 
        !           152: mpprobe(reg, vi)
        !           153:        caddr_t reg;
        !           154:        struct vba_device *vi;
        !           155: {
        !           156:        register int br, cvec;
        !           157:        register struct mpsoftc *ms;
        !           158: 
        !           159: #ifdef lint
        !           160:        br = 0; cvec = br; br = cvec;
        !           161:        mpintr(0);
        !           162:        mpdlintr(0);
        !           163: #endif
        !           164:        if (badaddr(reg, 2))
        !           165:                return (0);
        !           166:        ms = &mp_softc[vi->ui_unit];
        !           167:        /*
        !           168:         * Allocate page tables and mblok
        !           169:         * structure (mblok in non-cached memory).
        !           170:         */
        !           171:        if (vbainit(&ms->ms_buf, sizeof (struct mblok), VB_32BIT) == 0) {
        !           172:                printf("mp%d: vbainit failed\n", vi->ui_unit);
        !           173:                return (0);
        !           174:        }
        !           175:        ms->ms_mb = (struct mblok *)ms->ms_buf.vb_rawbuf;
        !           176:        ms->ms_ivec = MPINTRBASE + 2*vi->ui_unit;       /* XXX */
        !           177:        br = 0x14, cvec = ms->ms_ivec;                  /* XXX */
        !           178:        return (sizeof (*reg));
        !           179: }
        !           180: 
        !           181: mpattach(vi)
        !           182:        register struct vba_device *vi;
        !           183: {
        !           184:        register struct mpsoftc *ms = &mp_softc[vi->ui_unit];
        !           185: 
        !           186:        ms->ms_softCAR = vi->ui_flags;
        !           187:        /*
        !           188:         * Setup pointer to mblok, initialize bogus
        !           189:         * status block used by mpcc to locate the pointer
        !           190:         * and then poke the mpcc to get it to search host
        !           191:         * memory to find mblok pointer.
        !           192:         */
        !           193:        mpbogus.mbloks[vi->ui_unit] = (struct mblok *)ms->ms_buf.vb_physbuf;
        !           194:        *(short *)vi->ui_addr = 0x100;          /* magic */
        !           195: }
        !           196: 
        !           197: /*
        !           198:  * Open an mpcc port.
        !           199:  */
        !           200: /* ARGSUSED */
        !           201: mpopen(dev, mode)
        !           202:        dev_t dev;
        !           203: {
        !           204:        register struct tty *tp;
        !           205:        register struct mpsoftc *ms;
        !           206:        int error, s, port, unit, mpu;
        !           207:        struct vba_device *vi;
        !           208:        struct mpport *mp;
        !           209:        struct mpevent *ev;
        !           210: 
        !           211:        unit = minor(dev);
        !           212:        mpu = MPUNIT(unit);
        !           213:        if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0)
        !           214:                return (ENXIO);
        !           215:        tp = &mp_tty[unit];
        !           216:        if (tp->t_state & TS_XCLUDE && u.u_uid != 0)
        !           217:                return (EBUSY);
        !           218:        ms = &mp_softc[mpu];
        !           219:        port = MPPORT(unit);
        !           220:        if (ms->ms_mb->mb_proto[port] != MPPROTO_ASYNC ||
        !           221:            ms->ms_mb->mb_status != MP_OPOPEN)
        !           222:                return (ENXIO);
        !           223:        mp = &ms->ms_mb->mb_port[port];         /* host mpcc struct */
        !           224:        s = spl8();
        !           225:        /*
        !           226:         * serialize open and close events
        !           227:         */
        !           228:        while ((mp->mp_flags & MP_PROGRESS) || ((tp->t_state & TS_WOPEN) && 
        !           229:            !(mode&O_NONBLOCK) && !(tp->t_cflag&CLOCAL)))
        !           230:                if (error = tsleep((caddr_t)&tp->t_canq, TTIPRI | PCATCH,
        !           231:                    ttopen, 0)) {
        !           232:                        splx(s);
        !           233:                        return (error);
        !           234:                }
        !           235: restart:
        !           236:        tp->t_state |= TS_WOPEN;
        !           237:        tp->t_addr = (caddr_t)ms;
        !           238:        tp->t_oproc = mpstart;
        !           239:        tp->t_param = mpparam;
        !           240:        tp->t_dev = dev;
        !           241:        if ((tp->t_state & TS_ISOPEN) == 0) {
        !           242:                ttychars(tp);
        !           243:                if (tp->t_ispeed == 0) {
        !           244:                        tp->t_ispeed = TTYDEF_SPEED;
        !           245:                        tp->t_ospeed = TTYDEF_SPEED;
        !           246:                        tp->t_iflag = TTYDEF_IFLAG;
        !           247:                        tp->t_oflag = TTYDEF_OFLAG;
        !           248:                        tp->t_lflag = TTYDEF_LFLAG;
        !           249:                        tp->t_cflag = TTYDEF_CFLAG;
        !           250:                }
        !           251:                /*
        !           252:                 * Initialize port state: init MPCC interface
        !           253:                 * structures for port and setup modem control.
        !           254:                 */
        !           255:                error = mpportinit(ms, mp, port);
        !           256:                if (error)
        !           257:                        goto bad;
        !           258:                ev = mpparam2(tp, &tp->t_termios);
        !           259:                if (ev == 0) {
        !           260:                        error = ENOBUFS;
        !           261:                        goto bad;
        !           262:                }
        !           263:                mp->mp_flags |= MP_PROGRESS;
        !           264:                mpcmd(ev, EVCMD_OPEN, 0, ms->ms_mb, port);
        !           265:                /*
        !           266:                 * wait for port to start
        !           267:                 */
        !           268:                while (mp->mp_proto != MPPROTO_ASYNC)
        !           269:                        if (error = tsleep((caddr_t)&tp->t_canq,
        !           270:                            TTIPRI | PCATCH, ttopen, 0))
        !           271:                                goto bad;
        !           272:                ttsetwater(tp);
        !           273:                mp->mp_flags &= ~MP_PROGRESS;
        !           274:        }
        !           275:        while ((mode&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 &&  
        !           276:            (tp->t_state & TS_CARR_ON) == 0) {
        !           277:                if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
        !           278:                    ttopen, 0))
        !           279:                        goto bad;
        !           280:                /*
        !           281:                 * a mpclose() might have disabled port. if so restart
        !           282:                 */
        !           283:                if (mp->mp_proto != MPPROTO_ASYNC)
        !           284:                        goto restart;
        !           285:                tp->t_state |= TS_WOPEN;
        !           286:        }
        !           287:        error = (*linesw[tp->t_line].l_open)(dev,tp);
        !           288: done:
        !           289:        splx(s);
        !           290:        /*
        !           291:         * wakeup those processes waiting for the open to complete
        !           292:         */
        !           293:        wakeup((caddr_t)&tp->t_canq);
        !           294:        return (error);
        !           295: bad:
        !           296:        tp->t_state &= ~TS_WOPEN;
        !           297:        goto done;
        !           298: }
        !           299: 
        !           300: /*
        !           301:  * Close an mpcc port.
        !           302:  */
        !           303: /* ARGSUSED */
        !           304: mpclose(dev, flag)
        !           305:        dev_t dev;
        !           306: {
        !           307:        register struct tty *tp;
        !           308:        register struct mpport *mp;
        !           309:        register struct mpevent *ev;
        !           310:        int s, port, unit, error = 0;
        !           311:        struct mblok *mb;
        !           312: 
        !           313:        unit = minor(dev);
        !           314:        tp = &mp_tty[unit];
        !           315:        port = MPPORT(unit);
        !           316:        mb = mp_softc[MPUNIT(unit)].ms_mb;
        !           317:        mp = &mb->mb_port[port];
        !           318:        s = spl8();
        !           319:        if (mp->mp_flags & MP_PROGRESS) {
        !           320:                if (mp->mp_flags & MP_REMBSY) {
        !           321:                        mp->mp_flags &= ~MP_REMBSY;
        !           322:                        splx(s);
        !           323:                        return (0);
        !           324:                }
        !           325:                while (mp->mp_flags & MP_PROGRESS)
        !           326:                        if (error = tsleep((caddr_t)&tp->t_canq,
        !           327:                            TTIPRI | PCATCH, ttclos, 0)) {
        !           328:                                splx(s);
        !           329:                                return (error);
        !           330:                        }
        !           331:        }
        !           332:        mp->mp_flags |= MP_PROGRESS;
        !           333:        (*linesw[tp->t_line].l_close)(tp, flag);
        !           334:        ev = mp_getevent(mp, unit, 1);
        !           335:        if (ev == 0) {
        !           336:                error = ENOBUFS;
        !           337:                mp->mp_flags &= ~MP_PROGRESS;
        !           338:                goto out;
        !           339:        }
        !           340:        if (tp->t_state & TS_HUPCLS || (tp->t_state & TS_ISOPEN) == 0)
        !           341:                mpmodem(unit, MMOD_OFF);
        !           342:        else
        !           343:                mpmodem(unit, MMOD_ON);
        !           344:        mpcmd(ev, EVCMD_CLOSE, 0, mb, port);
        !           345:        error = ttyclose(tp);
        !           346: out:
        !           347:        if (mp->mp_flags & MP_REMBSY)
        !           348:                mpclean(mb, port);
        !           349:        else
        !           350:                while (mp->mp_flags & MP_PROGRESS && error == 0)
        !           351:                        error = tsleep((caddr_t)&tp->t_canq, TTIPRI | PCATCH,
        !           352:                            ttclos, 0);
        !           353:        splx(s);
        !           354:        return (error);
        !           355: }
        !           356: 
        !           357: /*
        !           358:  * Read from an mpcc port.
        !           359:  */
        !           360: mpread(dev, uio, flag)
        !           361:        dev_t dev;
        !           362:        struct uio *uio;
        !           363: {
        !           364:        struct tty *tp;
        !           365: 
        !           366:        tp = &mp_tty[minor(dev)];
        !           367:        return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
        !           368: }
        !           369: 
        !           370: /*
        !           371:  * Write to an mpcc port.
        !           372:  */
        !           373: mpwrite(dev, uio, flag)
        !           374:        dev_t dev;
        !           375:        struct uio *uio;
        !           376: {
        !           377:        struct tty *tp;
        !           378: 
        !           379:        tp = &mp_tty[minor(dev)];
        !           380:        return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
        !           381: }
        !           382: 
        !           383: /*
        !           384:  * Ioctl for a mpcc port
        !           385:  */
        !           386: mpioctl(dev, cmd, data, flag)
        !           387:        dev_t dev;
        !           388:        caddr_t data;
        !           389: {
        !           390:        register struct tty *tp;
        !           391:        register struct mpsoftc *ms;
        !           392:        register struct mpport *mp;
        !           393:        register struct mpevent *ev;
        !           394:        int s, port, error, unit;
        !           395:        struct mblok *mb;
        !           396: 
        !           397:        unit = minor(dev);
        !           398:        tp = &mp_tty[unit];
        !           399:        ms = &mp_softc[MPUNIT(unit)];
        !           400:        mb = ms->ms_mb;
        !           401:        port = MPPORT(unit);
        !           402:        mp = &mb->mb_port[port];
        !           403:        if (mp->mp_proto != MPPROTO_ASYNC)
        !           404:                return(ENXIO);
        !           405:        error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
        !           406:        if (error >= 0)
        !           407:                return (error);
        !           408:        error = ttioctl(tp, cmd, data, flag);
        !           409:        if (error >= 0)
        !           410:                return (error);
        !           411:        switch (cmd) {
        !           412:        case TIOCSBRK:                  /* send break */
        !           413:        case TIOCCBRK:                  /* clear break */
        !           414:                s = spl8();
        !           415:                while (mp->mp_flags & MP_IOCTL) {
        !           416:                        if (error = tsleep((caddr_t)&tp->t_canq,
        !           417:                            TTIPRI | PCATCH, ttyout, 0)) {
        !           418:                                splx(s);
        !           419:                                return (error);
        !           420:                        }
        !           421:                        if (mp->mp_proto != MPPROTO_ASYNC) {
        !           422:                                splx(s);
        !           423:                                return (ENXIO);
        !           424:                        }
        !           425:                }
        !           426:                ev = mp_getevent(mp, unit, 0);
        !           427:                if (ev) {
        !           428:                        mp->mp_flags |= MP_IOCTL;
        !           429:                        mpcmd(ev, EVCMD_IOCTL,
        !           430:                            (cmd == TIOCSBRK ? A_BRKON : A_BRKOFF), mb, port);
        !           431:                } else
        !           432:                        error = ENOBUFS;
        !           433:                splx(s);
        !           434:                break;
        !           435:        case TIOCSDTR:                  /* set dtr control line */
        !           436:                break;
        !           437:        case TIOCCDTR:                  /* clear dtr control line */
        !           438:                break;
        !           439:        default:
        !           440:                error = ENOTTY;
        !           441:                break;
        !           442:        }
        !           443:        return (error);
        !           444: }
        !           445: 
        !           446: mpparam(tp, t)
        !           447:        struct tty *tp;
        !           448:        struct termios *t;
        !           449: {
        !           450:        register struct mpevent *ev;
        !           451:        int unit = minor(tp->t_dev);
        !           452:        struct mpsoftc *ms = &mp_softc[MPUNIT(unit)];
        !           453:        struct mblok *mb = ms->ms_mb;
        !           454: 
        !           455:        ev = mpparam2(tp, t);
        !           456:        if (ev == 0)
        !           457:                return (ENOBUFS);
        !           458:        mpcmd(ev, EVCMD_IOCTL, A_CHGALL, mb, MPPORT(unit));
        !           459:        return (0);
        !           460: }      
        !           461: 
        !           462: struct mpevent *
        !           463: mpparam2(tp, t)
        !           464:        register struct tty *tp;
        !           465:        struct termios *t;
        !           466: {
        !           467:        register struct mpevent *ev;
        !           468:        register struct mpport *mp;
        !           469:        int unit = minor(tp->t_dev);
        !           470:        struct mblok *mb;
        !           471:        struct mpsoftc *ms;
        !           472:        register struct asyncparam *asp;
        !           473:        int port, speedcode;
        !           474: 
        !           475:        ms = &mp_softc[MPUNIT(unit)];
        !           476:        mb = ms->ms_mb;
        !           477:        port = MPPORT(unit);
        !           478:        mp = &mb->mb_port[port];
        !           479:        ev = mp_getevent(mp, unit, 0);  /* XXX */
        !           480:        speedcode = ttspeedtab(t->c_ospeed, mpspeedtab);
        !           481:        if (ev == 0 || speedcode < 0) {
        !           482: printf("mp mpunit %d port %d param2 failed ev: %x speed %d, wanted %d\n",
        !           483:                        MPUNIT(unit), port, ev, speedcode, t->c_ospeed);
        !           484:                return (0);     /* XXX */
        !           485:        }
        !           486:        /* YUCK */
        !           487:        asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1];
        !           488:        asp->ap_xon = t->c_cc[VSTART];
        !           489:        asp->ap_xoff = t->c_cc[VSTOP];
        !           490:        if (!(t->c_iflag&IXON) || (asp->ap_xon == _POSIX_VDISABLE) || 
        !           491:            (asp->ap_xoff == _POSIX_VDISABLE))
        !           492:                asp->ap_xena = MPA_DIS;
        !           493:        else
        !           494:                asp->ap_xena = MPA_ENA;
        !           495:        asp->ap_xany = ((t->c_iflag & IXANY) ? MPA_ENA : MPA_DIS);
        !           496: #ifdef notnow
        !           497:        if (t->t_cflag&CSIZE) == CS8) {
        !           498: #endif
        !           499:                asp->ap_data = MPCHAR_8;
        !           500:                asp->ap_parity = MPPAR_NONE;
        !           501: #ifdef notnow
        !           502:        } else {
        !           503:                asp->ap_data = MPCHAR_7;
        !           504:                if ((t->c_flags & (EVENP|ODDP)) == ODDP) /* XXX */
        !           505:                        asp->ap_parity = MPPAR_ODD;
        !           506:                else
        !           507:                        asp->ap_parity = MPPAR_EVEN;
        !           508:        }
        !           509: #endif
        !           510:        asp->ap_loop = MPA_DIS;         /* disable loopback */
        !           511:        asp->ap_rtimer = A_RCVTIM;      /* default receive timer */
        !           512:        if (t->c_ospeed == B110)
        !           513:                asp->ap_stop = MPSTOP_2;
        !           514:        else
        !           515:                asp->ap_stop = MPSTOP_1;
        !           516:        if (t->c_ospeed == 0) {
        !           517:                tp->t_state |= TS_HUPCLS;
        !           518:                setm(&asp->ap_modem, 0, DROP);
        !           519:                seti(&asp->ap_intena, A_DCD);
        !           520:                return (ev);
        !           521:        } 
        !           522:        if (t->c_ospeed == EXTA || t->c_ospeed == EXTB)
        !           523:                asp->ap_baud = M19200;
        !           524:        else
        !           525:                asp->ap_baud = speedcode;
        !           526:        if (1 || ms->ms_softCAR & (1<<port)) /* XXX HARDWIRE FOR NOW */
        !           527:                setm(&asp->ap_modem, A_DTR, ASSERT);
        !           528:        else
        !           529:                setm(&asp->ap_modem, A_DTR, AUTO);
        !           530:        seti(&asp->ap_intena, A_DCD);
        !           531:        return(ev);
        !           532: }
        !           533: 
        !           534: mpstart(tp)
        !           535:        register struct tty *tp;
        !           536: {
        !           537:        register struct mpevent *ev;
        !           538:        register struct mpport *mp;
        !           539:        struct mblok *mb;
        !           540:        struct mpsoftc *ms;
        !           541:        int port, unit, xcnt, n, s, i;
        !           542:        struct  hxmtl *hxp;
        !           543:        struct clist outq;
        !           544: 
        !           545:        s = spl8();
        !           546:        unit = minor(tp->t_dev);
        !           547:        ms = &mp_softc[MPUNIT(unit)];
        !           548:        mb = ms->ms_mb;
        !           549:        port = MPPORT(unit);
        !           550:        mp = &mb->mb_port[port];
        !           551:        hxp = &ms->ms_hxl[port];
        !           552:        xcnt = 0;
        !           553:        outq = tp->t_outq;
        !           554:        for (i = 0; i < MPXMIT; i++) {
        !           555:                if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
        !           556:                        break;
        !           557:                if (outq.c_cc <= tp->t_lowat) {
        !           558:                        if (tp->t_state & TS_ASLEEP) {
        !           559:                                tp->t_state &= ~TS_ASLEEP;
        !           560:                                wakeup((caddr_t)&tp->t_outq);
        !           561:                        }
        !           562:                        if (tp->t_wsel) {
        !           563:                                selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
        !           564:                                tp->t_wsel = 0;
        !           565:                                tp->t_state &= ~TS_WCOLL;
        !           566:                        }
        !           567:                }
        !           568:                if (outq.c_cc == 0)
        !           569:                        break;
        !           570:                /*
        !           571:                 * If we're not currently busy outputting,
        !           572:                 * and there is data to be output, set up
        !           573:                 * port transmit structure to send to mpcc.
        !           574:                 */
        !           575:                if (1) /* || tp->t_flags & (RAW|LITOUT))  XXX FIX */
        !           576:                        n = ndqb(&outq, 0);
        !           577:                else {
        !           578:                        n = ndqb(&outq, 0200);
        !           579:                        if (n == 0) {
        !           580:                                if (xcnt > 0)
        !           581:                                        break;
        !           582:                                n = getc(&outq);
        !           583:                                timeout(ttrstrt, (caddr_t)tp, (n&0177)+6);
        !           584:                                tp->t_state |= TS_TIMEOUT;
        !           585:                                break;
        !           586:                        }
        !           587:                }
        !           588:                hxp->dblock[i] = (caddr_t)kvtophys(outq.c_cf);
        !           589:                hxp->size[i] = n;
        !           590:                xcnt++;         /* count of xmts to send */
        !           591:                ndadvance(&outq, n);
        !           592:        }
        !           593:        /*
        !           594:         * If data to send, poke mpcc.
        !           595:         */
        !           596:        if (xcnt) {
        !           597:                ev = mp_getevent(mp, unit, 0);
        !           598:                if (ev == 0) {
        !           599:                        tp->t_state &= ~(TS_BUSY|TS_TIMEOUT);
        !           600:                } else {
        !           601:                        tp->t_state |= TS_BUSY;
        !           602:                        ev->ev_count = xcnt;
        !           603:                        mpcmd(ev, EVCMD_WRITE, 0, mb, MPPORT(unit));
        !           604:                }
        !           605:        }
        !           606:        splx(s);
        !           607: }
        !           608: 
        !           609: /*
        !           610:  * Advance cc bytes from q  but don't free memory.
        !           611:  */
        !           612: ndadvance(q, cc)
        !           613:        register struct clist *q;
        !           614:        register cc;
        !           615: {
        !           616:        register struct cblock *bp;
        !           617:        char *end;
        !           618:        int rem, s;
        !           619: 
        !           620:        s = spltty();
        !           621:        if (q->c_cc <= 0)
        !           622:                goto out;
        !           623:        while (cc>0 && q->c_cc) {
        !           624:                bp = (struct cblock *)((int)q->c_cf & ~CROUND);
        !           625:                if ((int)bp == (((int)q->c_cl-1) & ~CROUND)) {
        !           626:                        end = q->c_cl;
        !           627:                } else {
        !           628:                        end = (char *)((int)bp + sizeof (struct cblock));
        !           629:                }
        !           630:                rem = end - q->c_cf;
        !           631:                if (cc >= rem) {
        !           632:                        cc -= rem;
        !           633:                        q->c_cc -= rem;
        !           634:                        q->c_cf = bp->c_next->c_info;
        !           635:                } else {
        !           636:                        q->c_cc -= cc;
        !           637:                        q->c_cf += cc;
        !           638:                        break;
        !           639:                }
        !           640:        }
        !           641:        if (q->c_cc <= 0) {
        !           642:                q->c_cf = q->c_cl = NULL;
        !           643:                q->c_cc = 0;
        !           644:        }
        !           645: out:
        !           646:        splx(s);
        !           647: }
        !           648: 
        !           649: /*
        !           650:  * Stop output on a line, e.g. for ^S/^Q or output flush.
        !           651:  */
        !           652: /* ARGSUSED */
        !           653: mpstop(tp, rw)
        !           654:        register struct tty *tp;
        !           655:        int rw;
        !           656: {
        !           657:        register struct mpport *mp;
        !           658:        register struct mpevent *ev;
        !           659:        int unit = minor(tp->t_dev);
        !           660:        int port;
        !           661:        struct mblok *mb;
        !           662:        int s;
        !           663: 
        !           664:        s = spl8();
        !           665:        if (tp->t_state & TS_BUSY) {
        !           666:                if ((tp->t_state & TS_TTSTOP) == 0) {
        !           667:                        tp->t_state |= TS_FLUSH;
        !           668:                        port = MPPORT(unit);
        !           669:                        mb = mp_softc[MPUNIT(unit)].ms_mb;
        !           670:                        mp = &mb->mb_port[port];
        !           671:                        ev = mp_getevent(mp, unit, 0);
        !           672:                        if (ev == 0) {
        !           673:                                splx(s);
        !           674:                                return;
        !           675:                        }
        !           676:                        mpcmd(ev, EVCMD_WRITE, A_FLUSH, mb, port);
        !           677:                }
        !           678:        }
        !           679:        splx(s);
        !           680: }
        !           681: 
        !           682: /*
        !           683:  * Initialize an async port's MPCC state.
        !           684:  */
        !           685: mpportinit(ms, mp, port)
        !           686:        register struct mpsoftc *ms;
        !           687:        register struct mpport *mp;
        !           688:        int port;
        !           689: {
        !           690:        register struct mpevent *ev;
        !           691:        register int i;
        !           692:        caddr_t ptr;
        !           693: 
        !           694:        mp->mp_on = mp->mp_off = 0;
        !           695:        mp->mp_nextrcv = 0;
        !           696:        mp->mp_flags = 0;
        !           697:        ev = &mp->mp_recvq[0];
        !           698:        for (i = 0; ev < &mp->mp_recvq[MPINSET]; ev++, i++) {
        !           699:                ev->ev_status = EVSTATUS_FREE;
        !           700:                ev->ev_cmd = 0;
        !           701:                ev->ev_opts = 0;
        !           702:                ev->ev_error = 0;
        !           703:                ev->ev_flags = 0;
        !           704:                ev->ev_count = 0;
        !           705:                ev->ev_un.hxl = (struct hxmtl *) kvtophys(&ms->ms_hxl[port]);
        !           706:                ev->ev_params = (caddr_t) kvtophys(&ms->ms_async[port][i]);
        !           707:        }
        !           708:        ev = &mp->mp_sendq[0];
        !           709:        for (i = 0; ev < &mp->mp_sendq[MPOUTSET]; ev++, i++) {
        !           710:                /* init so that L2 can't send any events */
        !           711:                /* to host until open has completed      */
        !           712:                ev->ev_status = EVSTATUS_FREE;
        !           713:                ev->ev_cmd = 0;
        !           714:                ev->ev_opts = 0;
        !           715:                ev->ev_error = 0;
        !           716:                ev->ev_flags = 0;
        !           717:                ev->ev_count = 0;
        !           718:                ptr = (caddr_t) &ms->ms_cbuf[port][i][0];
        !           719:                ev->ev_un.rcvblk = (u_char *)kvtophys(ptr);
        !           720:                ev->ev_params = (caddr_t) kvtophys(ptr);
        !           721:        }
        !           722:        return (0);
        !           723: }
        !           724: 
        !           725: /*
        !           726:  * Send an event to an mpcc.
        !           727:  */
        !           728: mpcmd(ev, cmd, flags, mb, port)
        !           729:        register struct mpevent *ev;
        !           730:        struct mblok *mb;
        !           731: {       
        !           732:        int s;
        !           733: 
        !           734:        s = spl8();
        !           735:        /* move host values to inbound entry */
        !           736:        ev->ev_cmd = cmd;
        !           737:        ev->ev_opts = flags;
        !           738:        /* show event ready for mpcc */
        !           739:        ev->ev_status = EVSTATUS_GO;
        !           740:        mpintmpcc(mb, port);
        !           741:        splx(s);
        !           742: }
        !           743: 
        !           744: /*
        !           745:  * Return the next available event entry for the indicated port.
        !           746:  */
        !           747: struct mpevent *
        !           748: mp_getevent(mp, unit, cls_req)
        !           749:        register struct mpport *mp;
        !           750:        int unit;
        !           751:        int cls_req;
        !           752: {
        !           753:        register struct mpevent *ev;
        !           754:        int i, s;
        !           755: 
        !           756:        s = spl8();
        !           757:        ev = &mp->mp_recvq[mp->mp_on];
        !           758:        if (ev->ev_status != EVSTATUS_FREE)
        !           759:                goto bad;
        !           760:        /*
        !           761:         * If not a close request, verify one extra
        !           762:         * event is available for closing the port.
        !           763:         */
        !           764:        if (!cls_req) {
        !           765:                if ((i = mp->mp_on + 1) >= MPINSET)
        !           766:                        i = 0;
        !           767:                if (mp->mp_recvq[i].ev_status != EVSTATUS_FREE)
        !           768:                        goto bad;
        !           769:        }
        !           770:        /* init inbound fields marking this entry as busy */
        !           771:        ev->ev_cmd = 0;
        !           772:        ev->ev_opts = 0;
        !           773:        ev->ev_error = 0;
        !           774:        ev->ev_flags = 0;
        !           775:        ev->ev_count = 0;
        !           776:        ev->ev_status = EVSTATUS_BUSY;
        !           777:        /* adjust pointer to next available inbound entry */
        !           778:        adjptr(mp->mp_on, MPINSET);
        !           779:        splx(s);
        !           780:        return (ev);
        !           781: bad:
        !           782:        splx(s);
        !           783:        log(LOG_ERR, "mp%d: port%d, out of events\n",
        !           784:            MPUNIT(unit), MPPORT(unit));
        !           785:        return ((struct mpevent *)0);
        !           786: }
        !           787: 
        !           788: mpmodem(unit, flag)
        !           789:        int unit, flag;
        !           790: {
        !           791:        struct mpsoftc *ms = &mp_softc[MPUNIT(unit)];
        !           792:        int port = MPPORT(unit);
        !           793:        register struct mpport *mp;
        !           794:        register struct asyncparam *asp;
        !           795: 
        !           796:        mp = &ms->ms_mb->mb_port[port];
        !           797:        asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1];
        !           798:        if (flag == MMOD_ON) {
        !           799:                if (1 || ms->ms_softCAR & (1 << port))/* XXX HARDWIRE FOR NOW */
        !           800:                        setm(&asp->ap_modem, A_DTR, ASSERT);
        !           801:                else
        !           802:                        setm(&asp->ap_modem, A_DTR, AUTO);
        !           803:                seti(&asp->ap_intena, A_DCD);
        !           804:        } else {
        !           805:                setm(&asp->ap_modem, 0, DROP);
        !           806:                seti(&asp->ap_intena, 0);
        !           807:        }
        !           808: }
        !           809: 
        !           810: /*
        !           811:  * Set up the modem control structure according to mask.
        !           812:  * Each set bit in the mask means assert the corresponding
        !           813:  * modem control line, otherwise, it will be dropped.  
        !           814:  * RTS is special since it can either be asserted, dropped
        !           815:  * or put in auto mode for auto modem control.
        !           816:  */
        !           817: static
        !           818: setm(mc, mask, rts)
        !           819:        register struct mdmctl *mc;
        !           820:        register int mask;
        !           821: {
        !           822: 
        !           823:        mc->mc_rngdsr = (mask & A_RNGDSR) ? ASSERT : DROP;
        !           824:        mc->mc_rate = (mask & A_RATE) ? ASSERT : DROP;
        !           825:        mc->mc_dcd = (mask & A_DCD) ? ASSERT : DROP;
        !           826:        mc->mc_sectx = (mask & A_SECTX) ? ASSERT : DROP;
        !           827:        mc->mc_cts = (mask & A_CTS) ? ASSERT : DROP;
        !           828:        mc->mc_secrx = (mask & A_SECRX) ? ASSERT : DROP;
        !           829:        mc->mc_dtr = (mask & A_DTR) ? ASSERT : DROP;
        !           830:        mc->mc_rts = rts;
        !           831: }
        !           832: 
        !           833: /*
        !           834:  * Set up the status change enable field from mask.
        !           835:  * When a signal is enabled in this structure and
        !           836:  * and a change in state on a corresponding modem
        !           837:  * control line occurs, a status change event will
        !           838:  * be delivered to the host.
        !           839:  */
        !           840: static
        !           841: seti(mc, mask)
        !           842:        register struct mdmctl *mc;
        !           843:        register int mask;
        !           844: {
        !           845: 
        !           846:        mc->mc_rngdsr = (mask & A_RNGDSR) ? MDM_ON : MDM_OFF;
        !           847:        mc->mc_rate = (mask & A_RATE) ? MDM_ON : MDM_OFF;
        !           848:        mc->mc_dcd = (mask & A_DCD) ? MDM_ON : MDM_OFF;
        !           849:        mc->mc_sectx = (mask & A_SECTX) ? MDM_ON : MDM_OFF;
        !           850:        mc->mc_cts = (mask & A_CTS) ? MDM_ON : MDM_OFF;
        !           851:        mc->mc_secrx = (mask & A_SECRX) ? MDM_ON : MDM_OFF;
        !           852:        mc->mc_dtr = (mask & A_DTR) ? MDM_ON : MDM_OFF;
        !           853:        mc->mc_rts = (mask & A_RTS) ? MDM_ON : MDM_OFF;
        !           854: }
        !           855: 
        !           856: mpcleanport(mb, port)
        !           857:        struct mblok *mb;
        !           858:        int port;
        !           859: {
        !           860:        register struct mpport *mp;
        !           861:        register struct tty *tp;
        !           862: 
        !           863:        mp = &mb->mb_port[port];
        !           864:        if (mp->mp_proto == MPPROTO_ASYNC) {
        !           865:                mp->mp_flags = MP_REMBSY;
        !           866:                /* signal loss of carrier and close */
        !           867:                tp = &mp_tty[mb->mb_unit*MPCHUNK+port];
        !           868:                ttyflush(tp, FREAD|FWRITE);
        !           869:                (void) (*linesw[tp->t_line].l_modem)(tp, 0);
        !           870:        }
        !           871: }
        !           872: 
        !           873: mpclean(mb, port)
        !           874:        register struct mblok *mb;
        !           875:        int port;
        !           876: {
        !           877:        register struct mpport *mp;
        !           878:        register struct mpevent *ev;
        !           879:        register int i;
        !           880:        u_char list[2];
        !           881:        int unit;
        !           882: 
        !           883:        mp = &mb->mb_port[port];
        !           884:        unit = mb->mb_unit;
        !           885:        for (i = mp->mp_off; i != mp->mp_on; i = (i+1 % MPINSET)) {
        !           886:                ev = &mp->mp_recvq[i];
        !           887:                ev->ev_error = ENXIO;
        !           888:                ev->ev_status = EVSTATUS_DONE;
        !           889:        }
        !           890:        list[0] = port, list[1] = MPPORT_EOL;
        !           891:        mpxintr(unit, list);
        !           892:        mprintr(unit, list);
        !           893:        /* Clear async for port */
        !           894:        mp->mp_proto = MPPROTO_UNUSED;
        !           895:        mp->mp_flags = 0;
        !           896:        mp->mp_on = 0;
        !           897:        mp->mp_off = 0;
        !           898:        mp->mp_nextrcv = 0;
        !           899: 
        !           900:        mp_tty[unit*MPCHUNK + port].t_state = 0;
        !           901:        for (ev = &mp->mp_sendq[0]; ev < &mp->mp_sendq[MPOUTSET]; ev++) {
        !           902:                ev->ev_status = EVSTATUS_FREE;
        !           903:                ev->ev_cmd = 0;
        !           904:                ev->ev_error = 0;
        !           905:                ev->ev_un.rcvblk = 0;
        !           906:                ev->ev_params = 0;
        !           907:        }
        !           908:        for (ev = &mp->mp_recvq[0]; ev < &mp->mp_recvq[MPINSET]; ev++) {
        !           909:                ev->ev_status = EVSTATUS_FREE;
        !           910:                ev->ev_cmd = 0;
        !           911:                ev->ev_error = 0;
        !           912:                ev->ev_params = 0;
        !           913:        }
        !           914: }
        !           915: 
        !           916: /*
        !           917:  * MPCC interrupt handler.
        !           918:  */
        !           919: mpintr(mpcc)
        !           920:        int mpcc;
        !           921: {
        !           922:        register struct mblok *mb;
        !           923:        register struct his *his;
        !           924: 
        !           925:        mb = mp_softc[mpcc].ms_mb;
        !           926:        if (mb == 0) {
        !           927:                printf("mp%d: stray interrupt\n", mpcc);
        !           928:                return;
        !           929:        }
        !           930:        his = &mb->mb_hostint;
        !           931:        his->semaphore &= ~MPSEMA_AVAILABLE;
        !           932:        /*
        !           933:         * Check for events to be processed.
        !           934:         */
        !           935:        if (his->proto[MPPROTO_ASYNC].outbdone[0] != MPPORT_EOL)
        !           936:                mprintr(mpcc, his->proto[MPPROTO_ASYNC].outbdone);
        !           937:        if (his->proto[MPPROTO_ASYNC].inbdone[0] != MPPORT_EOL)
        !           938:                mpxintr(mpcc, his->proto[MPPROTO_ASYNC].inbdone);
        !           939:        if (mb->mb_harderr || mb->mb_softerr)
        !           940:                mperror(mb, mpcc);
        !           941:        his->semaphore |= MPSEMA_AVAILABLE;
        !           942: }
        !           943: 
        !           944: /*
        !           945:  * Handler for processing completion of transmitted events.
        !           946:  */
        !           947: mpxintr(unit, list)
        !           948:        register u_char *list;
        !           949: {
        !           950:        register struct mpport *mp;
        !           951:        register struct mpevent *ev;
        !           952:        register struct mblok *mb;
        !           953:        register struct tty *tp;
        !           954:        register struct asyncparam *ap;
        !           955:        struct mpsoftc *ms;
        !           956:        int port, i, j;
        !           957: #      define nextevent(mp) &mp->mp_recvq[mp->mp_off]
        !           958: 
        !           959:        ms = &mp_softc[unit];
        !           960:        mb = mp_softc[unit].ms_mb;
        !           961:        for (j = 0; j < MPMAXPORT && ((port = *list++) != MPPORT_EOL); j++) {
        !           962:                /*
        !           963:                 * Process each completed entry in the inbound queue.
        !           964:                 */
        !           965:                mp = &mb->mb_port[port];
        !           966:                tp = &mp_tty[unit*MPCHUNK + port];
        !           967:                ev = nextevent(mp);
        !           968:                for (; ev->ev_status & EVSTATUS_DONE; ev = nextevent(mp)) {
        !           969:                        /* YUCK */
        !           970:                        ap = &ms->ms_async[port][mp->mp_off];
        !           971:                        mppurge((caddr_t)ap, (int)sizeof (*ap));
        !           972:                        switch (ev->ev_cmd) {
        !           973:                        case EVCMD_OPEN:
        !           974:                                /*
        !           975:                                 * Open completion, start all reads and
        !           976:                                 * assert modem status information.
        !           977:                                 */
        !           978:                                for (i = 0; i < MPOUTSET; i++) 
        !           979:                                        mp->mp_sendq[i].ev_status = EVSTATUS_GO;
        !           980:                                (*linesw[tp->t_line].l_modem)
        !           981:                                    (tp, ap->ap_modem.mc_dcd == ASSERT);
        !           982:                                mp_freein(ev);
        !           983:                                adjptr(mp->mp_off, MPINSET);
        !           984:                                mp->mp_proto = MPPROTO_ASYNC;   /* XXX */
        !           985:                                wakeup((caddr_t)&tp->t_canq);
        !           986:                                break;
        !           987:                        case EVCMD_CLOSE:
        !           988:                                /*
        !           989:                                 * Close completion, flush all pending
        !           990:                                 * transmissions, free resources, and
        !           991:                                 * cleanup mpcc port state.
        !           992:                                 */
        !           993:                                for (i = 0; i < MPOUTSET; i++) {
        !           994:                                        mp->mp_sendq[i].ev_status =
        !           995:                                            EVSTATUS_FREE;
        !           996:                                        mp->mp_sendq[i].ev_un.rcvblk = 0;
        !           997:                                        mp->mp_sendq[i].ev_params = 0;
        !           998:                                }
        !           999:                                mp_freein(ev);
        !          1000:                                adjptr(mp->mp_off, MPINSET);
        !          1001:                                tp->t_state &= ~(TS_CARR_ON|TS_BUSY|TS_FLUSH);
        !          1002:                                mp->mp_on = mp->mp_off = mp->mp_nextrcv = 0;
        !          1003:                                mp->mp_flags &= ~MP_PROGRESS;
        !          1004:                                mp->mp_proto = MPPROTO_UNUSED;
        !          1005:                                wakeup((caddr_t)&tp->t_canq);
        !          1006:                                break;
        !          1007:                        case EVCMD_IOCTL:
        !          1008:                                mp_freein(ev);
        !          1009:                                adjptr(mp->mp_off, MPINSET);
        !          1010:                                mp->mp_flags &= ~MP_IOCTL;
        !          1011:                                wakeup((caddr_t)&tp->t_canq);
        !          1012:                                break;
        !          1013:                        case EVCMD_WRITE:
        !          1014:                                /*
        !          1015:                                 * Transmission completed, update tty
        !          1016:                                 * state and restart output.
        !          1017:                                 */
        !          1018:                                if (ev->ev_opts != A_FLUSH) {
        !          1019:                                        tp->t_state &= ~TS_BUSY;
        !          1020:                                        if (tp->t_state & TS_FLUSH)
        !          1021:                                                tp->t_state &= ~TS_FLUSH;
        !          1022:                                        else {
        !          1023:                                                register int cc = 0, n;
        !          1024:                                                struct hxmtl *hxp;
        !          1025: 
        !          1026:                                                hxp = &ms->ms_hxl[port];
        !          1027:                                                for (n=0;n < ev->ev_count; n++)
        !          1028:                                                        cc += hxp->size[n];
        !          1029:                                                ndflush(&tp->t_outq, cc);
        !          1030:                                        }
        !          1031:                                }
        !          1032:                                switch (ev->ev_error) {
        !          1033:                                case A_SIZERR:  /*# error in xmt data size */
        !          1034:                                        mplog(unit, port, A_XSIZE, 0);
        !          1035:                                        break;
        !          1036:                                case A_NXBERR:  /*# no more xmt evt buffers */
        !          1037:                                        mplog(unit, port, A_NOXBUF, 0);
        !          1038:                                        break;
        !          1039:                                }
        !          1040:                                mp_freein(ev);
        !          1041:                                adjptr(mp->mp_off, MPINSET);
        !          1042:                                mpstart(tp);
        !          1043:                                break;
        !          1044:                        default:
        !          1045:                                mplog(unit, port, A_INVCMD, (int)ev->ev_cmd);  
        !          1046:                                mp_freein(ev);
        !          1047:                                adjptr(mp->mp_off, MPINSET);
        !          1048:                                break;
        !          1049:                        }
        !          1050:                }
        !          1051:        }
        !          1052: #undef nextevent
        !          1053: }
        !          1054: 
        !          1055: mp_freein(ev)
        !          1056:        register struct mpevent *ev;
        !          1057: {
        !          1058:        /* re-init all values in this entry */
        !          1059:        ev->ev_cmd = 0;
        !          1060:        ev->ev_opts = 0;
        !          1061:        ev->ev_error = 0;
        !          1062:        ev->ev_flags = 0;
        !          1063:        ev->ev_count = 0;
        !          1064:        /* show this entry is available for use */
        !          1065:        ev->ev_status = EVSTATUS_FREE;
        !          1066: }
        !          1067: 
        !          1068: /*
        !          1069:  * Handler for processing received events.
        !          1070:  */
        !          1071: mprintr(unit, list)
        !          1072:        u_char *list;
        !          1073: {
        !          1074:        register struct tty *tp;
        !          1075:        register struct mpport *mp;
        !          1076:        register struct mpevent *ev;
        !          1077:        struct mblok *mb;
        !          1078:        register int cc;
        !          1079:        register char *cp;
        !          1080:        struct mpsoftc *ms;
        !          1081:        caddr_t ptr;
        !          1082:        char *rcverr;
        !          1083:        int port, i;
        !          1084: 
        !          1085:        ms = &mp_softc[unit];
        !          1086:        mb = mp_softc[unit].ms_mb;
        !          1087:        for (i = 0; i < MPMAXPORT && (port = *list++) != MPPORT_EOL; i++) {
        !          1088:                tp = &mp_tty[unit*MPCHUNK + port];
        !          1089:                mp = &mb->mb_port[port];
        !          1090:                ev = &mp->mp_sendq[mp->mp_nextrcv];
        !          1091:                while (ev->ev_status & EVSTATUS_DONE) {
        !          1092:                        switch(ev->ev_cmd) {
        !          1093:                        case EVCMD_STATUS:
        !          1094:                                /*
        !          1095:                                 * Status change, look for carrier changes.
        !          1096:                                 */
        !          1097:                                switch(ev->ev_opts) {
        !          1098:                                case DCDASRT:
        !          1099:                                        (*linesw[tp->t_line].l_modem)(tp, 1);
        !          1100:                                        wakeup((caddr_t)&tp->t_canq);
        !          1101:                                        break;
        !          1102:                                case DCDDROP:
        !          1103:                                        (*linesw[tp->t_line].l_modem)(tp, 0);
        !          1104:                                        wakeup((caddr_t)&tp->t_canq);
        !          1105:                                        break;
        !          1106:                                case NORBUF:
        !          1107:                                case NOEBUF:
        !          1108:                                        mplog(unit, port,
        !          1109:                                            "out of receive events", 0);
        !          1110:                                        break;
        !          1111:                                default:
        !          1112:                                        mplog(unit, port,
        !          1113:                                            "unexpect status command",
        !          1114:                                            (int)ev->ev_opts);
        !          1115:                                        break;
        !          1116:                                }
        !          1117:                                break;
        !          1118:                        case EVCMD_READ:
        !          1119:                                /*
        !          1120:                                 * Process received data.
        !          1121:                                 */
        !          1122:                                if ((tp->t_state & TS_ISOPEN) == 0) {
        !          1123:                                        wakeup((caddr_t)&tp->t_rawq);
        !          1124:                                        break;
        !          1125:                                }
        !          1126:                                if ((cc = ev->ev_count) == 0)
        !          1127:                                        break;
        !          1128:                                cp = ms->ms_cbuf[port][mp->mp_nextrcv];
        !          1129:                                mppurge(cp, CBSIZE);
        !          1130:                                while (cc-- > 0) {
        !          1131:                                        /*
        !          1132:                                         * A null character is inserted,
        !          1133:                                         * potentially when a break or framing
        !          1134:                                         * error occurs. If we're not in raw
        !          1135:                                         * mode, substitute the interrupt
        !          1136:                                         * character.
        !          1137:                                         */
        !          1138:                                        /*** XXX - FIXUP ***/
        !          1139:                                        if (*cp == 0 &&
        !          1140:                                            (ev->ev_error == BRKASRT ||
        !          1141:                                             ev->ev_error == FRAMERR))
        !          1142:                                                if ((tp->t_flags&RAW) == 0)
        !          1143:                                                        ;
        !          1144:                                                        /* XXX was break */
        !          1145:                                        (*linesw[tp->t_line].l_rint)(*cp++, tp);
        !          1146:                                }
        !          1147:                                /* setup for next read */
        !          1148:                                ptr = (caddr_t)&mp_softc[unit].ms_cbuf[port][mp->mp_nextrcv][0];
        !          1149:                                ev->ev_un.rcvblk = (u_char *)kvtophys(ptr);
        !          1150:                                ev->ev_params = (caddr_t) kvtophys(ptr);
        !          1151:                                switch(ev->ev_error) {
        !          1152:                                case RCVDTA:
        !          1153:                                        /* Normal (good) rcv data do not
        !          1154:                                         * report the following they are
        !          1155:                                         * "normal" errors 
        !          1156:                                         */
        !          1157:                                case FRAMERR:
        !          1158:                                        /* frame error */
        !          1159:                                case BRKASRT:
        !          1160:                                        /* Break condition */
        !          1161:                                case PARERR:
        !          1162:                                        /* parity error */
        !          1163:                                        rcverr = (char *)0;
        !          1164:                                        break;
        !          1165:                                case OVRNERR:
        !          1166:                                        /* Overrun error */
        !          1167:                                        rcverr = "overrun error";
        !          1168:                                        break;
        !          1169:                                case OVFERR:
        !          1170:                                        /* Overflow error */
        !          1171:                                        rcverr = "overflow error";
        !          1172:                                        break;
        !          1173:                                default:
        !          1174:                                        rcverr = "undefined rcv error";
        !          1175:                                        break;
        !          1176:                                }
        !          1177:                                if (rcverr != (char *)0)
        !          1178:                                        mplog(unit, port, rcverr,
        !          1179:                                              (int)ev->ev_error);
        !          1180:                                break;
        !          1181:                        default:
        !          1182:                                mplog(unit, port, "unexpected command",
        !          1183:                                        (int)ev->ev_cmd);
        !          1184:                                break;
        !          1185:                        }
        !          1186:                        ev->ev_cmd = 0;
        !          1187:                        ev->ev_opts = 0;
        !          1188:                        ev->ev_error = 0;
        !          1189:                        ev->ev_flags = 0;
        !          1190:                        ev->ev_count = 0;
        !          1191:                        ev->ev_status = EVSTATUS_GO;    /* start next read */
        !          1192:                        adjptr(mp->mp_nextrcv, MPOUTSET);
        !          1193:                        ev = &mp->mp_sendq[mp->mp_nextrcv];
        !          1194:                }
        !          1195:        }
        !          1196: }
        !          1197: 
        !          1198: /*
        !          1199:  * Log an mpcc diagnostic.
        !          1200:  */
        !          1201: mplog(unit, port, cp, flags)
        !          1202:        char *cp;
        !          1203: {
        !          1204: 
        !          1205:        if (flags)
        !          1206:                log(LOG_ERR, "mp%d: port%d, %s (%d)\n",
        !          1207:                    unit, port, cp, flags);
        !          1208:        else
        !          1209:                log(LOG_ERR, "mp%d: port%d, %s\n", unit, port, cp);
        !          1210: }
        !          1211: 
        !          1212: int    MPHOSTINT = 1;
        !          1213: 
        !          1214: mptimeint(mb)
        !          1215:        register struct mblok *mb;
        !          1216: {
        !          1217: 
        !          1218:         mb->mb_mpintcnt = 0;
        !          1219:         mb->mb_mpintclk = (caddr_t)0;
        !          1220:        *(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2;
        !          1221: }
        !          1222: 
        !          1223: /*
        !          1224:  * Interupt mpcc
        !          1225:  */
        !          1226: mpintmpcc(mb, port)
        !          1227:        register struct mblok *mb;
        !          1228: {
        !          1229: 
        !          1230:         mb->mb_intr[port] |= MPSEMA_WORK;
        !          1231:         if (++mb->mb_mpintcnt == MPHOSTINT) {
        !          1232:                 mb->mb_mpintcnt = 0;
        !          1233:                *(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2;
        !          1234:                 if (mb->mb_mpintclk) {
        !          1235:                         untimeout(mptimeint, (caddr_t)mb);
        !          1236:                         mb->mb_mpintclk = 0;
        !          1237:                 }
        !          1238:         } else {
        !          1239:                 if (mb->mb_mpintclk == 0) {
        !          1240:                         timeout(mptimeint, (caddr_t)mb, 4);
        !          1241:                         mb->mb_mpintclk = (caddr_t)1;
        !          1242:                 }
        !          1243:         }
        !          1244: }
        !          1245: 
        !          1246: static char *mpherrmsg[] = {
        !          1247:        "",
        !          1248:        "Bus error",                            /* MPBUSERR */
        !          1249:        "Address error",                        /* ADDRERR */
        !          1250:        "Undefined ecc interrupt",              /* UNDECC */
        !          1251:        "Undefined interrupt",                  /* UNDINT */
        !          1252:        "Power failure occurred",               /* PWRFL */
        !          1253:        "Stray transmit done interrupt",        /* NOXENTRY */
        !          1254:        "Two fast timers on one port",          /* TWOFTMRS */
        !          1255:        "Interrupt queue full",                 /* INTQFULL */
        !          1256:        "Interrupt queue ack error",            /* INTQERR */
        !          1257:        "Uncorrectable dma parity error",       /* CBPERR */
        !          1258:        "32 port ACAP failed power up",         /* ACPDEAD */
        !          1259: };
        !          1260: #define        NHERRS  (sizeof (mpherrmsg) / sizeof (mpherrmsg[0]))
        !          1261: 
        !          1262: mperror(mb, unit)
        !          1263:        register struct mblok *mb;
        !          1264:        int unit;
        !          1265: {
        !          1266:        register char *cp;
        !          1267:        register int i;
        !          1268: 
        !          1269:        if (mb->mb_softerr) {
        !          1270:                switch (mb->mb_softerr) {
        !          1271:                case DMAPERR:   /* dma parity error */
        !          1272:                        cp = "dma parity error";
        !          1273:                        break;
        !          1274:                case ECCERR:
        !          1275:                        cp = "local memory ecc error";
        !          1276:                        break;
        !          1277:                default:
        !          1278:                        cp = "unknown error";
        !          1279:                        break;
        !          1280:                }
        !          1281:                log(LOG_ERR, "mp%d: soft error, %s", unit, cp);
        !          1282:                mb->mb_softerr = 0;
        !          1283:        }
        !          1284:        if (mb->mb_harderr) {
        !          1285:                if (mb->mb_harderr < NHERRS)
        !          1286:                        cp = mpherrmsg[mb->mb_harderr];
        !          1287:                else
        !          1288:                        cp = "unknown error";
        !          1289:                log(LOG_ERR, "mp%d: hard error, %s", unit, cp);
        !          1290:                if (mb->mb_status == MP_OPOPEN) {
        !          1291:                        for (i = 0; i < MPMAXPORT; i++) {
        !          1292:                                mpcleanport(mb, i);
        !          1293:                                mb->mb_proto[i] = MPPROTO_UNUSED;
        !          1294:                        }
        !          1295:                }
        !          1296:                mb->mb_harderr = 0;
        !          1297:                mb->mb_status = 0;
        !          1298:        }
        !          1299: }
        !          1300: 
        !          1301: mppurge(addr, cc)
        !          1302:        register caddr_t addr;
        !          1303:        register int cc;
        !          1304: {
        !          1305: 
        !          1306:        for (; cc >= 0; addr += NBPG, cc -= NBPG)
        !          1307:                mtpr(P1DC, addr);
        !          1308: }
        !          1309: 
        !          1310: /*
        !          1311:  * MPCC Download Pseudo-device.
        !          1312:  */
        !          1313: char   mpdlbuf[MPDLBUFSIZE];
        !          1314: int    mpdlbusy;               /* interlock on download buffer */
        !          1315: int    mpdlerr;
        !          1316: 
        !          1317: mpdlopen(dev)
        !          1318:        dev_t dev;
        !          1319: {
        !          1320:        int unit, mpu;
        !          1321:        struct vba_device *vi;
        !          1322: 
        !          1323:        unit = minor(dev);
        !          1324:        mpu = MPUNIT(unit);
        !          1325:        if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0)
        !          1326:                return (ENODEV);
        !          1327:        return (0);
        !          1328: }
        !          1329: 
        !          1330: mpdlwrite(dev, uio)
        !          1331:        dev_t dev;
        !          1332:        struct uio *uio;
        !          1333: {
        !          1334:        register struct mpsoftc *ms = &mp_softc[MPUNIT(minor(dev))];
        !          1335:        register struct mpdl *dl;
        !          1336:        int error;
        !          1337: 
        !          1338:        if (ms->ms_mb == 0 || ms->ms_mb->mb_status != MP_DLOPEN)
        !          1339:                return (EFAULT);
        !          1340:        dl = &ms->ms_mb->mb_dl;
        !          1341:        dl->mpdl_count = uio->uio_iov->iov_len;
        !          1342:        dl->mpdl_data = (caddr_t) kvtophys(mpdlbuf);
        !          1343:        if (error = uiomove(mpdlbuf, (int)dl->mpdl_count, uio))
        !          1344:                return (error);
        !          1345:        uio->uio_resid -= dl->mpdl_count;    /* set up return from write */
        !          1346:        dl->mpdl_cmd = MPDLCMD_NORMAL;
        !          1347:        error = mpdlwait(dl);
        !          1348:        return (error);
        !          1349: }
        !          1350: 
        !          1351: mpdlclose(dev)
        !          1352:        dev_t dev;
        !          1353: {
        !          1354:        register struct mblok *mb = mp_softc[MPUNIT(minor(dev))].ms_mb;
        !          1355: 
        !          1356:        if (mb == 0 || mb->mb_status != MP_DLDONE) {
        !          1357:                mpbogus.status = 0;
        !          1358:                if (mpbogus.mb == mpbogus.mbloks[MPUNIT(minor(dev))])
        !          1359:                        mpdlbusy--;
        !          1360:                return (EEXIST);
        !          1361:        }
        !          1362:        mb->mb_status = MP_OPOPEN;
        !          1363:        mpbogus.status = 0;
        !          1364:        /* set to dead, for board handshake */
        !          1365:        mb->mb_hostint.imok = MPIMOK_DEAD;
        !          1366:        return (0);
        !          1367: }
        !          1368: 
        !          1369: /* ARGSUSED */
        !          1370: mpdlioctl(dev, cmd, data, flag)
        !          1371:        dev_t dev;
        !          1372:        caddr_t data;
        !          1373: {
        !          1374:        register struct mblok *mb;
        !          1375:        register struct mpdl *dl;
        !          1376:        int unit, error = 0, s, i;
        !          1377: 
        !          1378:        mb = mp_softc[unit=MPUNIT(minor(dev))].ms_mb;
        !          1379:        if (mb == 0)
        !          1380:                 return (EEXIST);
        !          1381:        dl = &mb->mb_dl;
        !          1382:        error = 0;
        !          1383:        switch (cmd) {
        !          1384:        case MPIOPORTMAP:
        !          1385:                bcopy(data, (caddr_t)mb->mb_proto, sizeof (mb->mb_proto));
        !          1386:                break;
        !          1387:        case MPIOHILO:
        !          1388:                bcopy(data, (caddr_t)&mb->mb_hiport, 2*(sizeof(mb->mb_hiport)));
        !          1389:                break;
        !          1390:        case MPIOENDDL:
        !          1391:                dl->mpdl_count = 0;
        !          1392:                dl->mpdl_data = 0;
        !          1393:                dl->mpdl_cmd = MPIOENDDL&IOCPARM_MASK;
        !          1394:                error = mpdlwait(dl);
        !          1395:                mpccinit(unit);
        !          1396:                mb->mb_status = MP_DLDONE;
        !          1397:                mpdlbusy--;
        !          1398:                break;
        !          1399:        case MPIOENDCODE:
        !          1400:                dl->mpdl_count = 0;
        !          1401:                dl->mpdl_data = 0;
        !          1402:                dl->mpdl_cmd = MPIOENDCODE&IOCPARM_MASK;
        !          1403:                error = mpdlwait(dl);
        !          1404:                break;
        !          1405:        case MPIOASYNCNF:
        !          1406:                bcopy(data, mpdlbuf, sizeof (struct abdcf));
        !          1407:                dl->mpdl_data = (caddr_t) kvtophys(mpdlbuf);
        !          1408:                dl->mpdl_count = sizeof (struct abdcf);
        !          1409:                dl->mpdl_cmd = MPIOASYNCNF&IOCPARM_MASK;
        !          1410:                error = mpdlwait(dl);
        !          1411:                break;
        !          1412:        case MPIOSTARTDL:
        !          1413:                s = spl8();
        !          1414:                while (mpdlbusy)
        !          1415:                        if (error = tsleep((caddr_t)&mpdlbusy,
        !          1416:                            (PZERO+1) | PCATCH, devioc, 0))
        !          1417:                                break;
        !          1418:                splx(s);
        !          1419:                if (error)
        !          1420:                        break;
        !          1421:                mpdlbusy++;
        !          1422:                /* initialize the downloading interface */
        !          1423:                mpbogus.magic = MPMAGIC;
        !          1424:                mpbogus.mb = mpbogus.mbloks[unit];
        !          1425:                mpbogus.status = 1;
        !          1426:                dl->mpdl_status = EVSTATUS_FREE;
        !          1427:                dl->mpdl_count = 0;
        !          1428:                dl->mpdl_cmd = 0;
        !          1429:                dl->mpdl_data = (char *) 0;
        !          1430:                mpdlerr = 0;
        !          1431:                mb->mb_magic = MPMAGIC;
        !          1432:                mb->mb_ivec = mp_softc[unit].ms_ivec+1; /* download vector */
        !          1433:                mb->mb_status = MP_DLPEND;
        !          1434:                mb->mb_diagswitch[0] = 'A';
        !          1435:                mb->mb_diagswitch[1] = 'P';
        !          1436:                s = spl8();
        !          1437:                *(u_short *)mpinfo[unit]->ui_addr = 2;
        !          1438:                error = tsleep((caddr_t)&mb->mb_status, (PZERO+1) | PCATCH,
        !          1439:                    devio, 30*hz);
        !          1440:                splx(s);
        !          1441:                if (error == EWOULDBLOCK)
        !          1442:                        error = ETIMEDOUT;
        !          1443:                if (error)
        !          1444:                        mpbogus.status = 0;
        !          1445:                bzero((caddr_t)mb->mb_port, sizeof (mb->mb_port));
        !          1446:                break;
        !          1447:        case MPIORESETBOARD:
        !          1448:                s = spl8();
        !          1449:                if (mb->mb_imokclk)
        !          1450:                        mb->mb_imokclk = 0;
        !          1451:                *(u_short *)mpinfo[unit]->ui_addr = 0x100;
        !          1452:                if (mb->mb_status == MP_DLOPEN || mb->mb_status == MP_DLDONE) {
        !          1453:                        mpdlerr = MP_DLERROR;
        !          1454:                        dl->mpdl_status = EVSTATUS_FREE;
        !          1455:                        wakeup((caddr_t)&dl->mpdl_status);
        !          1456:                        mpbogus.status = 0;
        !          1457:                }
        !          1458:                for (i = 0; i < MPMAXPORT; i++) {
        !          1459:                        if (mb->mb_harderr || mb->mb_softerr)
        !          1460:                                mperror(mb, i);
        !          1461:                        mpcleanport(mb, i);
        !          1462:                        mb->mb_proto[i] = MPPROTO_UNUSED;
        !          1463:                }
        !          1464:                mb->mb_status = 0;
        !          1465:                splx(s);
        !          1466:                break;
        !          1467:        default:
        !          1468:                error = EINVAL;
        !          1469:                break;
        !          1470:        }
        !          1471:        return (error);
        !          1472: }
        !          1473: 
        !          1474: mpccinit(unit)
        !          1475:        int unit;
        !          1476: {
        !          1477:         register struct mblok *mb = mp_softc[unit].ms_mb;
        !          1478:         register struct his *his;
        !          1479:         register int i, j;
        !          1480: 
        !          1481:         mb->mb_status = MP_DLDONE;
        !          1482:         mb->mb_ivec = mp_softc[unit].ms_ivec;
        !          1483:         mb->mb_magic = MPMAGIC;
        !          1484:         /* Init host interface structure */
        !          1485:         his = &mb->mb_hostint;
        !          1486:         his->semaphore = MPSEMA_AVAILABLE;
        !          1487:         for (i = 0; i < NMPPROTO; i++)
        !          1488:                 for (j = 0; j < MPMAXPORT; j++) {
        !          1489:                         his->proto[i].inbdone[j] = MPPORT_EOL;
        !          1490:                         his->proto[i].outbdone[j] = MPPORT_EOL;
        !          1491:                 }
        !          1492:         mb->mb_unit = unit;
        !          1493: }
        !          1494: 
        !          1495: mpdlintr(mpcc)
        !          1496:        int mpcc;
        !          1497: {
        !          1498:        register struct mblok *mb;
        !          1499:        register struct mpdl *dl;
        !          1500: 
        !          1501:        mb = mp_softc[mpcc].ms_mb;
        !          1502:        if (mb == 0) {
        !          1503:                printf("mp%d: stray download interrupt\n", mpcc);
        !          1504:                return;
        !          1505:        }
        !          1506:        dl = &mb->mb_dl;
        !          1507:        switch (mb->mb_status) {
        !          1508:        case MP_DLOPEN:
        !          1509:                if (dl->mpdl_status != EVSTATUS_DONE)
        !          1510:                        mpdlerr = MP_DLERROR;
        !          1511:                dl->mpdl_status = EVSTATUS_FREE;
        !          1512:                wakeup((caddr_t)&dl->mpdl_status);
        !          1513:                return;
        !          1514:        case MP_DLPEND:
        !          1515:                mb->mb_status = MP_DLOPEN;
        !          1516:                wakeup((caddr_t)&mb->mb_status);
        !          1517:                /* fall thru... */
        !          1518:        case MP_DLTIME:
        !          1519:                return;
        !          1520:        case MP_OPOPEN:
        !          1521:                if (mb->mb_imokclk)
        !          1522:                        mb->mb_imokclk = 0;
        !          1523:                mb->mb_nointcnt = 0;            /* reset no interrupt count */
        !          1524:                mb->mb_hostint.imok = MPIMOK_DEAD;
        !          1525:                mb->mb_imokclk = (caddr_t)1;
        !          1526:                break;
        !          1527:        default:
        !          1528:                log(LOG_ERR, "mp%d: mpdlintr, status %x\n",
        !          1529:                    mpcc, mb->mb_status);
        !          1530:                break;
        !          1531:        }
        !          1532: }
        !          1533: 
        !          1534: /* 
        !          1535:  * Wait for a transfer to complete or a timeout to occur.
        !          1536:  */
        !          1537: mpdlwait(dl)
        !          1538:        register struct mpdl *dl;
        !          1539: {
        !          1540:        int s, error = 0;
        !          1541: 
        !          1542:        s = spl8();
        !          1543:        dl->mpdl_status = EVSTATUS_GO;
        !          1544:        while (dl->mpdl_status != EVSTATUS_FREE) {
        !          1545:                error = tsleep((caddr_t)&dl->mpdl_status, (PZERO+1) | PCATCH,
        !          1546:                    devout, 0);
        !          1547:                if (mpdlerr == MP_DLERROR)
        !          1548:                        error = EIO;
        !          1549:                if (error)
        !          1550:                        break;
        !          1551:        }
        !          1552:        splx(s);
        !          1553:        return (error);
        !          1554: }
        !          1555: #endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.