/* * Copyright (c) 1985 by Sun Microsystems, Inc. */ #include "ie.h" #if NIE > 0 /* * Sun Intel Ethernet Controller interface */ #include "../h/param.h" #include "../h/systm.h" #include "../machine/pte.h" #include "../h/map.h" #include "../h/buf.h" #include "../h/vmmac.h" #include "../h/conf.h" #include "../h/ttyio.h" #include "../h/enio.h" #include "../h/ttyld.h" #include "../h/stream.h" #include "../h/ethernet.h" #include "../sundev/mbvar.h" #include "../sundev/iereg.h" #include "../sundev/iem.h" #include "../sundev/ieob.h" /* controller types */ #define IE_MB 1 /* Multibus */ #define IE_OB 2 /* Onboard I/O */ #define IEOBMEMDFT (40*1024) /* default memory allocated for obie */ #define CBCACHESIZ (80) /* size of control block cache */ #define OVFLWARNMASK (3) /* overflow count mask before warning */ #define NPOOL_RBD 50 #define NTRANSBUF 4 /* NUmber of transmit buffers */ int ieprobe(), ieattach(), iepoll(); struct mb_device *ieinfo[NIE]; struct mb_driver iedriver = { ieprobe, 0, ieattach, 0, 0, iepoll, /* take the larger of the two devices */ sizeof (struct mie_device), "ie", ieinfo, }; caddr_t from_ieaddr(); ieoff_t to_ieoff(); caddr_t from_ieoff(); ieaddr_t to_ieaddr(); long escbget(), escbput(); ieint_t to_ieint(); #define from_ieint to_ieint #define iepages(x) (((x) + IEPAGSIZ-1)>>IEPAGSHIFT) int iecbcsmall; int iecbclarge; /* * Ethernet software status per interface. */ struct ie_softc { /* Added by D.A.K for version 9 compatability */ char myetheradr[6]; char attached; /* Non zero when ie has been attached */ int state; int collisions; int ierrors, oerrors; int ipackets, opackets; struct ietfd *es_tfd[NTRANSBUF]; /* transmit TFD */ struct ietbd *es_tbd[NTRANSBUF]; /* transmit TBD */ caddr_t es_tbuffer[NTRANSBUF]; /* transmit buffer */ int es_tfree; /* transmitter bit map */ /* Supplied by SUN */ struct mie_device *es_mie; /* Multibus board registers */ struct obie_device *es_obie; /* On-board registers */ caddr_t es_memspace; /* + chip addr = kernel addr */ int es_paddr; /* starting page number for board */ int es_vmemsize; /* size of multibus mem port */ caddr_t es_base; /* our addr of control block base */ struct map *es_cbmap; /* rmap for control blocks */ struct map *es_memmap; /* rmap for memory */ struct map *es_pgmap; /* rmap for 1K page (ND) memory accessed thru if_memmap */ struct iescb *es_scb; /* SCB ptr */ struct iescp *es_scp; /* SCP ptr */ struct iecb *es_cbhead; /* CBL head */ struct iecb *es_cbtail; /* CBL tail */ struct ierfd *es_rfdhead; /* head of RFD list */ struct ierfd *es_rfdtail; /* tail of RFD list */ struct ierbd *es_rbdhead; /* head of RBD list */ struct ierbd *es_rbdtail; /* tail of RBD list */ struct ieipack *es_iepavail; /* standby input packets */ int es_obmem; /* total IE_OB main memory */ char es_type; /* type of controller */ char es_simple; /* doing simple flag */ u_int es_runotready; /* RU was not ready counter */ u_int es_xmiturun; /* xmit DMA underrun counter */ u_int es_ieheart; /* heartbeat counter */ u_int es_iedefer; /* deferred transmission counter */ struct ieierr { u_int crc; u_int aln; u_int rsc; u_int ovr; } es_ierr; /* input error counters */ int es_cbsize; /* control block area size */ int es_cbc_lo; /* small block cache pointer */ int es_cbc_hi; /* large block cache pointer */ u_int es_cbc_ovfl; /* overflow counter */ int es_cbcbusy; /* cache is loaded */ long es_cbcache[CBCACHESIZ]; /* control block cache */ } ie_softc[NIE]; #define NIECHAN (CHANS_PER_UNIT * NIE) struct iechan { int unit; int packets; struct queue *ieq; int type; } iechan[NIECHAN]; int nodev(), ieopen(), ieclose(), ieput(), iesrv(); static struct qinit ierinit = { nodev, NULL, ieopen, ieclose, 0, 0 }; static struct qinit iewinit = { ieput, NULL, ieopen, ieclose, 1514, 0 }; /* 1514 bytes is minimum highwater mark to send 1514 byte packets */ struct streamtab iesinfo = { &ierinit, &iewinit }; #define IEMAPSIZ 100 struct map iecbmap[NIE][IEMAPSIZ]; struct map iememmap[NIE][IEMAPSIZ]; struct map iepgmap[NIE][IEMAPSIZ]; #define escballoc(es, type, cached) (type *)escbget(es, sizeof(type), cached) #define escbfree(es, ptr) escbput(es, sizeof(*ptr), (long)ptr); #define escbpin(es, len, addr) rmget(es->es_cbmap, (int)len, (int)addr) #define esmemalloc(es, len) rmalloc(es->es_memmap, (long)len) #define esmemfree(es, len, ptr) rmfree(es->es_memmap, (long)len, (long)ptr) #define esmempin(es, len, addr) rmget(es->es_memmap, (int)len, (int)addr) /* * fixed header size for page alignment of received data buffers * this is sizeof(struct ndpack) for ND. (doesn't include ether header) */ #define OPTHDRSIZ 48 #define IPACKOVH 8 /* struct ieipack header overhead */ struct ieipack { struct ieipack *iep_next; /* next in list; MUST BE FIRST FIELD */ struct ie_softc *iep_es; /* assoc ie_softc */ char iep_data[1500]; /* the packet */ }; #define IEDELAY 400000 /* delay period (in ms) before giving up */ #define IEKLUDGE 20 /* delay period (in ms) to make chip work */ /* * Probe for device. */ ieprobe(reg, unit) caddr_t reg; { register short *sp; struct ie_softc *es = &ie_softc[unit]; if ((getkpgmap(reg) & PGT_MASK) == PGT_OBIO) { register struct obie_device *obie = (struct obie_device *)reg; /* onboard Ethernet */ if (pokec(reg, 0)) return (0); if (peekc(reg) == -1) return (0); if (obie->obie_noreset) return (0); es->es_type = IE_OB; } else { register struct mie_device *mie = (struct mie_device *)reg; /* Multibus Ethernet */ sp = (short *)mie; if (poke(sp, 0)) return (0); sp = &mie->mie_prom[0]; if (poke(sp, 0x6789)) return (0); if (peek(sp) == 0x6789) return (0); es->es_type = IE_MB; } return (1); } /* * Interface exists; make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. */ ieattach(md) struct mb_device *md; { struct ie_softc *es = &ie_softc[md->md_unit]; if (md->md_intr) /* set up vectored interrupts */ *(md->md_intr->v_vptr) = (int)es; if (es->es_type == IE_OB) { es->es_obie = (struct obie_device *)md->md_addr; es->es_obmem = IEOBMEMDFT; } else { es->es_mie = (struct mie_device *)md->md_addr; /* * magic thru config used to artificially restrict * the amount of MBMEM used, as there is only 1 Mb * available, making it a scarce resource. */ switch (md->md_flags) { case 0: default: es->es_vmemsize = 256*1024; break; case 1: es->es_vmemsize = 128*1024; break; case 2: es->es_vmemsize = 64*1024; break; } } iechipreset(es); localetheraddr(NULL, es->myetheradr); ieinit(md->md_unit); es->attached = 1; } /* ARGSUSED */ ieopen(q, dev) register struct queue *q; register dev_t dev; { register struct iechan *ied; register struct ie_softc *es; register unit; dev = minor(dev); unit = dev / CHANS_PER_UNIT; if (dev >= NIECHAN) return(0); if (unit >= NIE) return(0); es = &ie_softc[unit]; if(!es->attached) return(0); ied = &iechan[dev]; if (ied->ieq) return(0); ied->unit = unit; ied->ieq = q; q->ptr = (caddr_t)ied; q->flag |= QBIGB | QDELIM; WR(q)->ptr = (caddr_t)ied; WR(q)->flag |= QBIGB|QDELIM; return(1); } ieclose(q) register struct queue *q; { register struct iechan *ied; ied = (struct iechan *)q->ptr; flushq(WR(q), 1); ied->ieq = NULL; } ieput(q, bp) register struct queue *q; register struct block *bp; { register struct iechan *ied; register struct ie_softc *es; register unit, s; ied = (struct iechan *)q->ptr; unit = ied->unit; es = &ie_softc[unit]; switch(bp->type) { case M_IOCTL: ieioctl(q, bp); return; case M_DATA: putq(q, bp); return; case M_DELIM: break; default: freeb(bp); return; } putq(q, bp); s = splie(); ied->packets++; if (es->es_tfree) iestartout(unit); splx(s); } /* * Process an ioctl request. */ ieioctl(q, bp) register struct queue *q; register struct block *bp; { register struct iechan *ied; register struct ie_softc *es; register u_char *msg; int unit, cmd; ied = (struct iechan *)q->ptr; unit = ied->unit; es = &ie_softc[unit]; cmd = ((union stmsg *)(bp->rptr))->ioc1.com; msg = (u_char *)&((union stmsg *)(bp->rptr))->ioc1.sb; bp->type = M_IOCACK; switch (cmd) { case ENIOADDR: /* get my Ethernet address */ bcopy(es->myetheradr, (int *)msg, 6); break; case ENIOTYPE: ied->type = *(int *)msg; break; default: bp->type = M_IOCNAK; break; } qreply(q, bp); } /* * Unconditionally restart the interface from ground zero. */ ieinit(unit) int unit; { struct ie_softc *es = &ie_softc[unit]; int s = splie(); iechipreset(es); iedomaps(es); if (!iechipinit(es)) goto exit; iedoaddr(es); /* * Hang out receive buffers and start any pending writes. */ ierustart(es); iesplice(es); iestartout(unit); exit: (void) splx(s); } /* * Handle Polling Ethernet interrupts */ iepoll() { register struct ie_softc *es; register int found = 0; for (es = ie_softc; es < &ie_softc[NIE]; es++) { switch (es->es_type) { case IE_MB: if (es->es_mie->mie_pie && es->es_mie->mie_pe) { ieparity(es); return (1); } if (es->es_mie->mie_ie == 0) continue; if (es->es_mie->mie_intr) found = 1; break; case IE_OB: if (es->es_obie->obie_buserr) panic("ie: bus error"); if (es->es_obie->obie_ie == 0) continue; if (es->es_obie->obie_intr) found = 1; break; default: /* not present */ continue; } /* * Since the 82586 can take away an interrupt * request after presenting it to the processsor * (to facilitate an update of a new interrupt * condition in the scb), we also have to check * the scb to see if it indicates an interrupt * condition from the chip. */ if (found == 0) { register struct iescb *scb = es->es_scb; if (scb->ie_cx || scb->ie_fr || scb->ie_cnr || scb->ie_rnr) found = 1; } if (found) { ieintr(es); break; } } return (found); } /* * Handle Ethernet interrupts */ ieintr(es) register struct ie_softc *es; { register struct iescb *scb; register int cmd, unit; unit = es - ie_softc; scb = es->es_scb; iechkcca(scb); if (scb->ie_cmd != 0) { register struct mb_device *md; printf("ie%d: lost synch\n", unit); iestat(es); if (unit >= NIE || (md = ieinfo[unit]) == 0 || md->md_alive == 0) return; ieinit(unit); return; } cmd = 0; /* * This can be done faster with something like: * cmd = (*(short *)scb->ie_cx) & * (IECMD_ACK_CX|IECMD_ACK_FR|IECMD_ACK_CNR|IECMD_ACK_RNR); */ if (scb->ie_cx) cmd |= IECMD_ACK_CX; if (scb->ie_fr) cmd |= IECMD_ACK_FR; if (scb->ie_cnr) cmd |= IECMD_ACK_CNR; if (scb->ie_rnr) cmd |= IECMD_ACK_RNR; if (cmd == 0) { printf("ie%d: spurious intr\n", unit); cmd = IECMD_ACK_CX+IECMD_ACK_FR+IECMD_ACK_CNR+IECMD_ACK_RNR; } scb->ie_cmd = cmd; ieca(es); if (cmd & (IECMD_ACK_RNR|IECMD_ACK_FR)) ierecv(es); if (cmd & (IECMD_ACK_CNR|IECMD_ACK_CX)) iecbdone(es); if (es->es_cbhead && scb->ie_cus == IECUS_IDLE) { iechkcca(scb); if (es->es_cbhead && scb->ie_cus == IECUS_IDLE && !scb->ie_cx && !scb->ie_cnr) printf("ie%d: CU out of synch\n", unit); } } /* * Tell the chip its ethernet address */ iedoaddr(es) register struct ie_softc *es; { register struct ieiaddr *iad; iad = escballoc(es, struct ieiaddr, 1); if (iad == 0) panic("iedoaddr: iad"); bzero((caddr_t)iad, sizeof (struct ieiaddr)); iad->ieia_cb.ie_cmd = IE_IADDR; bcopy(es->myetheradr, iad->ieia_addr, sizeof(es->myetheradr)); iesimple(es, &iad->ieia_cb); escbfree(es, iad); } /* * Move info from driver toward protocol interface */ ieread(es, rfd) register struct ie_softc *es; register struct ierfd *rfd; { int unit = es - ie_softc; register struct etherpup *header; register struct ierbd *rbd; register struct iechan *ied; register struct queue *q; register struct block *bp; register len, min, i; register u_char *p; short type; if (!rfd->ierfd_ok) { es->ierrors++; printf("ie%d: receive error\n", unit); return; } if (rfd->ierfd_rbd == IENORBD) { printf("ie%d: runt packet\n", unit); es->ierrors++; return; } rbd = (struct ierbd *)from_ieoff(es, (ieoff_t)rfd->ierfd_rbd); if (!rbd->ierbd_eof) { /* length > 1500 */ printf("ie%d: giant packet\n", unit); es->ierrors++; return; } /* * Pull packet off interface. */ header = (struct etherpup *)rfd->ierfd_dhost; type = header->type; ied = &iechan[unit * CHANS_PER_UNIT]; for (i = 0; i < CHANS_PER_UNIT; i++, ied++) if (ied->ieq && ied->type == type) break; if (i >= CHANS_PER_UNIT) return; q = ied->ieq; if (q->next->flag & QFULL) { es->ierrors++; return; } len = (rbd->ierbd_cnthi << 8) + rbd->ierbd_cntlo; /* * Splice in the ethernet header * Assumes allocb will return a block at least as big as etherpup. */ bp = allocb(len + sizeof(struct etherpup)); *(struct etherpup *)bp->wptr = *header; bp->wptr += sizeof(struct etherpup); /* * Now copy the data */ p = (u_char *)from_ieaddr(es, rbd->ierbd_buf); i = MIN(((bp->lim - bp->base) - sizeof(struct etherpup)), len); for(;;) { len -= i; while (i-- > 0) *bp->wptr++ = *p++; (*q->next->qinfo->putp)(q->next, bp); if (len <= 0) break; bp = allocb(len); i = MIN((bp->lim - bp->base), len); } if (putctl(q->next, M_DELIM)) es->ipackets++; else printf("ierint no DELIM bp\n"); } /* * Process completed input packets * and recycle resources; * only called from interrupt level by ieintr */ ierecv(es) register struct ie_softc *es; { register struct ierfd *rfd, *nrfd; register struct ierbd *rbd, *nrbd; register struct iescb *scb = es->es_scb; int eof, e; rfd = es->es_rfdhead; if (rfd == NULL) /* not initialized */ return; top: while (rfd && rfd->ierfd_done) { ieread(es, rfd); if (rfd->ierfd_rbd != IENORBD) { rbd = (struct ierbd *)from_ieoff(es, (ieoff_t)rfd->ierfd_rbd); if (rbd != es->es_rbdhead) panic("ierecv rbd"); while (rbd && rbd->ierbd_used) { if (rbd != (struct ierbd *)from_ieoff(es, (ieoff_t)es->es_rbdtail->ierbd_next)) panic("ierecv rbd list"); nrbd = (struct ierbd *)from_ieoff(es, (ieoff_t)rbd->ierbd_next); rbd->ierbd_el = 1; eof = rbd->ierbd_eof; *(short *)rbd = 0; es->es_rbdtail->ierbd_el = 0; es->es_rbdtail = rbd; rbd = es->es_rbdhead = nrbd; if (eof) break; } } if (rfd != (struct ierfd *)from_ieoff(es, (ieoff_t)es->es_rfdtail->ierfd_next)) panic("ierecv rfd list"); nrfd = (struct ierfd *)from_ieoff(es, (ieoff_t)rfd->ierfd_next); rfd->ierfd_rbd = IENORBD; rfd->ierfd_el = 1; *(short *)rfd = 0; es->es_rfdtail->ierfd_el = 0; es->es_rfdtail = rfd; rfd = es->es_rfdhead = nrfd; } if (e = scb->ie_crcerrs) { /* count of CRC errors */ scb->ie_crcerrs = 0; e = from_ieint(e); es->ierrors += e; es->es_ierr.crc += e; } if (e = scb->ie_alnerrs) { /* count of alignment errors */ scb->ie_alnerrs = 0; e = from_ieint(e); es->ierrors += e; es->es_ierr.aln += e; } if (e = scb->ie_rscerrs) { /* count of discarded packets */ scb->ie_rscerrs = 0; e = from_ieint(e); es->ierrors += e; es->es_ierr.rsc += e; } if (e = scb->ie_ovrnerrs) { /* count of overrun packets */ scb->ie_ovrnerrs = 0; e = from_ieint(e); es->ierrors += e; es->es_ierr.ovr += e; } if (scb->ie_rus == IERUS_READY) /* as expected */ return; es->es_runotready++; /* following test must be made when we know chip is quiet */ if (es->es_rfdhead->ierfd_done) /* more snuck in */ goto top; es->es_rfdhead->ierfd_rbd = to_ieoff(es, (caddr_t)es->es_rbdhead); iechkcca(scb); scb->ie_rfa = to_ieoff(es, (caddr_t)es->es_rfdhead); scb->ie_cmd = IECMD_RU_START; ieca(es); } /* * Free up the resources after transmitting a packet. * Called by iecuclean at splie or hardware level 3. */ iexmitdone(es, td) register struct ie_softc *es; register struct ietfd *td; { int unit = es - ie_softc; register int i; if (td->ietfd_ok) { es->collisions += td->ietfd_ncoll; es->opackets++; if (td->ietfd_defer) es->es_iedefer++; if (td->ietfd_heart) es->es_ieheart++; } else { es->oerrors++; if (td->ietfd_xcoll) printf("ie%d: Ethernet jammed\n", unit); if (td->ietfd_nocarr) printf("ie%d: no carrier\n", unit); if (td->ietfd_nocts) printf("ie%d: no CTS\n", unit); if (td->ietfd_underrun) es->es_xmiturun++; } if (!td->ietfd_tbd) panic("ie%d: iexmitdone: no tbd"); for(i = 0; i < NTRANSBUF; i++) if (td == es->es_tfd[i]) break; if (es->es_tfree & (1 << i)) printf("ie%d stray xmit interrupt\n", unit); es->es_tfree |= (1 << i); } #define rndtoeven(x) (((x)+1) & ~1) /* * Start or restart output to wire. */ iestartout(unit) int unit; { register struct ie_softc *es = &ie_softc[unit]; register struct iechan *ied; register cnt, i; register caddr_t to; int count, bufnum; struct ietfd *td; register struct ietbd *tbd; register struct queue *q; register struct block *bp, *nbp; struct block *head, **bnext; if (!es->es_tfree) goto out; ied = &iechan[unit * CHANS_PER_UNIT]; for(i = 0; i < CHANS_PER_UNIT; i++, ied++) if (ied->ieq && ied->packets > 0) break; if (i >= CHANS_PER_UNIT) goto out; ied->packets--; q = WR(ied->ieq); /* The packet must be justified to the end of the buffer. * Therefore, we have to count the bytes before copying. */ bnext = &head; while (*bnext = bp = getq(q)) { if (bp->type == M_DELIM) { bp->next = 0; break; } cnt += bp->wptr - bp->rptr; bnext = &bp->next; } bp = head; /* if there is no ethernet header in packet, throw it out */ if (cnt < sizeof(struct etherpup) || (bp->wptr - bp->rptr) < sizeof(struct etherpup)) { while(bp) { nbp = bp->next; freeb(bp); bp = nbp; } goto out; } for (bufnum = 0; ; bufnum++) if (es->es_tfree & (1 << bufnum)) break; es->es_tfree &= ~(1 << bufnum); td = es->es_tfd[bufnum]; tbd = es->es_tbd[bufnum]; /* Setup the header */ bcopy(bp->rptr, (caddr_t)td->ietfd_dhost, 6); /* Dest */ bp->rptr += 12; /* Skip src */ bcopy(bp->rptr, (caddr_t)&td->ietfd_type, 2); /* Type */ bp->rptr += 2; /* Skip src */ cnt -= sizeof(struct etherpup); /* Setup the data */ if (cnt > 1500) /* test for too large packets */ cnt = 1500; count = cnt; to = es->es_tbuffer[bufnum]; while (cnt > 0 && bp != 0) { i = bp->wptr - bp->rptr; bcopy(bp->rptr, to, i); cnt -= i; to += i; nbp = bp->next; freeb(bp); bp = nbp; } if (count < 46) /* test for small packets */ count = 46; /* flush remaining buffers, if any (too large packet: count > 1500) */ while (bp) { nbp = bp->next; freeb(bp); bp = nbp; } /* Setup the buffer descriptor */ tbd->ietbd_eof = 1; tbd->ietbd_next = 0; tbd->ietbd_buf = to_ieaddr(es, es->es_tbuffer[bufnum]); tbd->ietbd_cntlo = count & 0xFF; tbd->ietbd_cnthi = count >> 8; td->ietfd_tbd = to_ieoff(es, (caddr_t)tbd); td->ietfd_cmd = IE_TRANSMIT; iedocb(es, (struct iecb *)td); out: iecustart(es); return; } /* * Set the control block area size */ iesetcbsize(es) register struct ie_softc *es; { int tfds; /* number of tfd's */ int tfdsize; /* cbsize for each tfd */ int tbufsize; /* cbsize for each tbuf */ int rbufs; /* number of rbuf's in cb area */ int rbufsize; /* cbsize for each rbuf */ int rfds; /* number of rfd's */ int rfdsize; /* cbsize for each rfd */ int fixed; /* fixed overhead, including slop */ fixed = sizeof (struct iescp) + sizeof (struct ieiscp) + sizeof (struct iescb) + sizeof (struct ieconf) + 100; tfdsize = sizeof (struct ietfd) + sizeof (struct ietbd); tbufsize = sizeof (struct ieipack); rbufsize = sizeof (struct ieipack); rfdsize = sizeof (struct ierfd) + sizeof (struct ierbd); switch (es->es_type) { case IE_OB: tfds = NTRANSBUF; rbufs = (es->es_obmem - fixed - tfds * (tfdsize+tbufsize)) / (rbufsize + rfdsize); break; case IE_MB: tfds = 50; /* maximum if_snd */ /* each rbuf eats into a 2K section of vmemsize */ rbufs = min(NPOOL_RBD, (u_int)(es->es_vmemsize>>11)); break; } rfds = rbufs; es->es_cbsize = fixed + tfds * tfdsize + rfds * rfdsize; } /* * Called by ieinit to (allocate and re)initialize rmap's * We need to be careful, since the board may be in use * (ieipacks loaned out, pages swapped) */ iedomaps(es) register struct ie_softc *es; { int unit = es - ie_softc; iesetcbsize(es); bzero((caddr_t)iecbmap[unit], sizeof iecbmap[unit]); bzero((caddr_t)iememmap[unit], sizeof iememmap[unit]); es->es_cbhead = NULL; es->es_cbcbusy = NULL; iecbcinit(es); if (es->es_type == IE_OB) { int memall(); caddr_t va; if (es->es_base == 0) { va = wmemall(memall, es->es_obmem); if (va == 0) panic("ieattach: no memory"); es->es_base = va; } va = es->es_base; es->es_cbmap = &iecbmap[unit][0]; es->es_memmap = es->es_cbmap; rminit(es->es_cbmap, (long)es->es_obmem, (long)es->es_base, "iecb", IEMAPSIZ); es->es_memspace = (caddr_t)KERNELBASE; #ifdef sun3 /* use the page already set up by the prom monitor */ es->es_scp = (struct iescp *)(IESCPADDR+es->es_memspace); #else /* * Remap the [preallocated] virtual page containing * the SCP to make it point to the same physical * location as va, so that we can muck with the * control blocks using the address returned from * memall, and the chip can locate the scp at its * fixed address * Cache note: since we can not, in general, enforce * the cache antialiasing separation requirement, we * would need to avoid caching this physical page */ { struct pte dummypte; /* for mapin to write on */ int paddr = getkpgmap(va) & PG_PFNUM; mapin(&dummypte, (u_int)btop(IESCPADDR+ es->es_memspace), (u_int)paddr, 1, PG_V | PG_KW); es->es_scp = (struct iescp *)escbpin(es, (long)sizeof (struct iescp), (long)es->es_base + (IESCPADDR & PGOFSET)); } #endif sun3 } else { /* IE_MB */ register struct mie_device *mie = es->es_mie; struct miepg *pg; short *ap; int i; int a, vaddr, paddr; int firsttime = es->es_memspace == 0; if (firsttime) { if ((a = rmalloc(kernelmap,(long)btoc(es->es_vmemsize))) == 0) { printf("ie%d: no kernelmap for ie memory\n", unit); panic("iedomaps"); } es->es_memspace = (caddr_t)kmxtob(a); } vaddr = (int)es->es_memspace; a = btokmx((struct pte *)vaddr); /* board's mem boundary to byte addr */ paddr = mie->mie_mbmhi << 16; /* preserve pagetype bits and which megabyte its in (for VME) */ es->es_paddr = getkpgmap((caddr_t)mie) & PG_PFNUM; es->es_paddr &= ~(0x100000/NBPG -1); es->es_paddr |= btop(paddr); mapin(&Usrptmap[a], (u_int)btop(vaddr), (u_int)es->es_paddr, (int)btoc(es->es_vmemsize), PG_V | PG_KW); /* clear the board unless in use */ if (firsttime) { ap = (short *)mie->mie_pgmap; for (i=0; i<IEVVSIZ; i++) /* clears mp_p2mem */ *ap++ = 0; for (i=0; i<IEPMEMSIZ/IEPAGSIZ; i++) { mie->mie_pgmap[0].mp_pfnum = i; bzero(es->es_memspace, IEPAGSIZ); } pg = &mie->mie_pgmap[0]; for (i=0; i<es->es_vmemsize/IEPAGSIZ; i++) { pg->mp_swab = 1; pg->mp_pfnum = i; pg++; } /* use last onboard ie page for chip init */ /* (no need to reclaim, since beyond vmemsize) */ pg = &mie->mie_pgmap[IEVVSIZ-1]; pg->mp_swab = 1; pg->mp_pfnum = 0; /* patch potential powerup parity problem */ mie->mie_peack = 1; } es->es_base = es->es_memspace; es->es_cbmap = &iecbmap[unit][0]; rminit(es->es_cbmap, (long)es->es_cbsize, (long)es->es_base, "iecb", IEMAPSIZ); es->es_scp = (struct iescp *)escbpin(es, (long)sizeof (struct iescp), (long)es->es_base + (IESCPADDR & (IEPAGSIZ-1))); es->es_memmap = &iememmap[unit][0]; es->es_pgmap = &iepgmap[unit][0]; /* * if we have enough memory, create a page pool which * can manipulated via if_memmap, say by ND */ if (es->es_vmemsize == 256*1024) { rminit(es->es_memmap, (long)(128*1024-es->es_cbsize), (long)(es->es_memspace+es->es_cbsize), "iemem", IEMAPSIZ); if (firsttime) rminit(es->es_pgmap, (long)(128*1024), (long)(es->es_memspace+128*1024), "iepg", IEMAPSIZ); } else { rminit(es->es_memmap, (long)(es->es_vmemsize-es->es_cbsize), (long)(es->es_memspace+es->es_cbsize), "iemem", IEMAPSIZ); rminit(es->es_pgmap, (long)0, (long)0, "iepg", IEMAPSIZ); } } } /* * Basic 82586 initialization */ int iechipinit(es) register struct ie_softc *es; { int unit = es - ie_softc; struct ieiscp *iscp; struct iescb *scb; struct iecb *cb; struct ieconf *ic; int ok = 0; int gotintr; int i; #ifdef notdef struct ietdr *tdr; #endif if (es->es_scp == 0) { printf("ie%d: scp alloc failed\n", unit); goto exit; } iscp = escballoc(es, struct ieiscp, 0); if (iscp == 0) { printf("ie%d: iscp alloc failed\n", unit); goto exit; } scb = escballoc(es, struct iescb, 0); if (scb == 0) { printf("ie%d: scb alloc failed\n", unit); goto exit; } es->es_scb = scb; reset: bzero((caddr_t)es->es_scp, sizeof (struct iescp)); es->es_scp->ie_iscp = to_ieaddr(es, (caddr_t)iscp); bzero((caddr_t)iscp, sizeof (struct ieiscp)); iscp->ie_busy = 1; iscp->ie_cbbase = to_ieaddr(es, es->es_base); iscp->ie_scb = to_ieoff(es, (caddr_t)scb); bzero((caddr_t)scb, sizeof (struct iescb)); scb->ie_magic = IEMAGIC; /* * Hardware reset the chip. We make the interval from * reset to initial channel attention as small as reasonable * to reduce the risk of scribbling chips getting us. */ switch (es->es_type) { case IE_MB: /* hardware reset already occurred in iechipreset */ break; case IE_OB: es->es_obie->obie_noreset = 1; DELAY(IEKLUDGE); /* REQUIRED */ break; } ieca(es); CDELAY(!iscp->ie_busy, IEDELAY); /* ensure chip eats iscp */ CDELAY(scb->ie_cnr, IEDELAY); /* ensure scb updated too */ gotintr = iewaitintr(es); /* wait for interrupt */ if (iscp->ie_busy || !scb->ie_cnr || !gotintr) { printf("ie%d: init failed:%s%s%s\n", unit, iscp->ie_busy?" iscp busy":"", !scb->ie_cnr ?" no cnr":"", !gotintr ?" no intr":""); goto exit; } if (scb->ie_cus != IECUS_IDLE ) { printf("ie%d: cus not idle after reset\n", unit); iechipreset(es); goto reset; } cb = escballoc(es, struct iecb, 1); if (cb == NULL) { printf("ie%d: cb alloc failed\n", unit); goto exit; } bzero((caddr_t)cb, sizeof (struct iecb)); cb->ie_cmd = IE_DIAGNOSE; iesimple(es, cb); if (!cb->ie_ok) { printf("ie%d: Intel 82586 failed diagnostics\n", unit); escbfree(es, cb); goto exit; } escbfree(es, cb); #ifdef notdef /* skip, since hardware requires quiet net to work */ tdr = escballoc(es, struct ietdr, 1); if (tdr == NULL) { printf("ie%d: tdr alloc failed\n", unit); goto exit; } bzero((caddr_t)tdr, sizeof (struct ietdr)); tdr->ietdr_cb.ie_cmd = IE_TDR; iesimple(es, &tdr->ietdr_cb); if (!tdr->ietdr_ok) { #define TDRCONST 12 /* approx 0.77c/10Mhz */ int dist = (tdr->ietdr_timhi<<8)+tdr->ietdr_timlo; if (dist != 0x7FF) printf("ie%d: link not OK - distance = ~%dm\n", unit, TDRCONST*dist); else printf("ie%d: link not OK\n", unit); escbfree(es, tdr); goto exit; } if (tdr->ietdr_xcvr) printf("ie%d: transceiver bad\n", unit); if (tdr->ietdr_open) printf("ie%d: net not terminated\n", unit); if (tdr->ietdr_shrt) printf("ie%d: net shorted\n", unit); escbfree(es, tdr); #endif notdef ic = escballoc(es, struct ieconf, 1); if (ic == NULL) { printf("ie%d: ic alloc failed\n", unit); goto exit; } iedefaultconf(ic); iesimple(es, &ic->ieconf_cb); escbfree(es, ic); for (i = 0; i < NTRANSBUF; i++) { if ((es->es_tfd[i] = escballoc(es, struct ietfd, 0)) == NULL) { printf("ie%d: tfd alloc failed\n", unit); goto exit; } if ((es->es_tbd[i] = escballoc(es, struct ietbd, 0)) == NULL) { printf("ie%d: tbd alloc failed\n", unit); goto exit; } if ((es->es_tbuffer[i]=(caddr_t)esmemalloc(es, 1500)) ==NULL) { printf("ie%d: tbuffer alloc failed\n", unit); goto exit; } es->es_tfree |= (1 << i); } ok = 1; exit: return (ok); } /* * called by ierustart to create the receiver buffer list */ iegetrbufs(es) register struct ie_softc *es; { caddr_t addr; int count, avail; register int page, last; register struct ieipack *iep; es->es_iepavail = NULL; if (es->es_type == IE_OB) { last = (es->es_obmem-es->es_cbsize) / (sizeof (struct ieipack)) - NTRANSBUF; for (count = 0; count < last; count++) { iep = (struct ieipack *)esmemalloc(es, sizeof (struct ieipack)); if (iep == 0) break; iep->iep_es = es; iep->iep_next = es->es_iepavail; es->es_iepavail = iep; } avail = count; } else { /* IE_MB */ count = NPOOL_RBD; /* XXX not really, since there could be page pool */ last = es->es_vmemsize >> IEPAGSHIFT; /* 2 pages for xmit (vice receive) buffer */ page = iepages(es->es_cbsize) + 2; for (; count > 0 && page < last; page++) { addr = es->es_memspace + page*IEPAGSIZ; addr += IEPAGSIZ - (IPACKOVH + OPTHDRSIZ); if (!esmempin(es, sizeof (struct ieipack), (int)addr)) continue; count--; iep = (struct ieipack *)addr; iep->iep_es = es; iep->iep_next = es->es_iepavail; es->es_iepavail = iep; } avail = NPOOL_RBD - count; } return (avail); } /* * Initialize and start the Receive Unit */ ierustart(es) register struct ie_softc *es; { int unit = es - ie_softc; register struct ierbd *rbd; register struct ierfd *rfd; register struct iescb *scb; register struct ieipack *iep; register int i, nrfd, ninit_rbd; ninit_rbd = iegetrbufs(es); es->es_rbdhead = NULL; for (i = 0; i < ninit_rbd; i++) { if ((iep = es->es_iepavail) == NULL) break; rbd = escballoc(es, struct ierbd, 0); if (rbd == NULL) break; es->es_iepavail = iep->iep_next; *(short *)rbd = 0; if (es->es_rbdhead) { rbd->ierbd_next = to_ieoff(es, (caddr_t)es->es_rbdhead); rbd->ierbd_el = 0; } else { es->es_rbdtail = rbd; rbd->ierbd_next = 0; rbd->ierbd_el = 1; } es->es_rbdhead = rbd; rbd->ierbd_buf = to_ieaddr(es, iep->iep_data); rbd->ierbd_sizehi = 1500 >> 8; rbd->ierbd_sizelo = 1500 & 0xFF; rbd->ierbd_iep = iep; } /* * We allocate one fewer RFD than RBD to * avoid a suspected microcode bug in the chip */ nrfd = i-1; es->es_rbdtail->ierbd_next = to_ieoff(es, (caddr_t)es->es_rbdhead); es->es_rfdhead = NULL; for (i=0; i<nrfd; i++) { rfd = escballoc(es, struct ierfd, 0); if (rfd == NULL) { break; } *(short *)rfd = 0; if (es->es_rfdhead) { rfd->ierfd_next = to_ieoff(es, (caddr_t)es->es_rfdhead); rfd->ierfd_el = 0; } else { es->es_rfdtail = rfd; rfd->ierfd_next = 0; rfd->ierfd_el = 1; } es->es_rfdhead = rfd; rfd->ierfd_susp = 0; rfd->ierfd_rbd = IENORBD; } if (i != nrfd) printf("ie%d: fewer RFD's were allocated than expected\n", unit); es->es_rfdtail->ierfd_next = to_ieoff(es, (caddr_t)es->es_rfdhead); rfd = es->es_rfdhead; rfd->ierfd_rbd = to_ieoff(es, (caddr_t)rbd); scb = es->es_scb; if (scb->ie_rus != IERUS_IDLE) { printf("ie%d: RU not idle??\n", unit); iestat(es); iechkcca(scb); scb->ie_cmd = IECMD_RU_ABORT; ieca(es); } iechkcca(scb); scb->ie_rfa = to_ieoff(es, (caddr_t)rfd); scb->ie_cmd = IECMD_RU_START; ieca(es); CDELAY(scb->ie_rus == IERUS_READY, IEDELAY); if (scb->ie_rus != IERUS_READY) printf("ie%d: RU did not become ready\n", unit); } /* * Put a CB on the CBL */ iedocb(es, cb) register struct ie_softc *es; register struct iecb *cb; { int s = splie(); *(short *)cb = 0; /* clear status bits */ cb->ie_susp = 0; /* clear suspend bit */ cb->ie_el = 1; /* will be reset in iecustart */ cb->ie_intr = 1; cb->ie_next = 0; if (es->es_cbhead) { es->es_cbtail->ie_next = to_ieoff(es, (caddr_t)cb); es->es_cbtail = cb; } else { es->es_cbhead = es->es_cbtail = cb; } (void) splx(s); } /* * Process completed CBs, reclaiming specified storage. * Allocator is responsible for reclaiming other storage. * Called by iecustart at splie. * Called by iecbdone at splie or hardware level 3. */ iecuclean(es) register struct ie_softc *es; { register struct iecb *cb; while ((cb = es->es_cbhead) && cb->ie_done) { if (cb->ie_next) es->es_cbhead = (struct iecb *)from_ieoff(es, (ieoff_t)cb->ie_next); else es->es_cbhead = NULL; switch (cb->ie_cmd) { case IE_TRANSMIT: iexmitdone(es, (struct ietfd *)cb); break; default: if (!es->es_simple) printf("ie%d: unknown cmd %x done\n", es-ie_softc, cb->ie_cmd); break; } } } /* * Start the CU with the current CBL */ iecustart(es) register struct ie_softc *es; { register struct iecb *cb; register struct iescb *scb = es->es_scb; int s = splie(); iechkcca(scb); if (es->es_cbhead == NULL || scb->ie_cus == IECUS_READY) { /* still going */ (void) splx(s); return; } iecuclean(es); /* link remaining CBs into continuous list */ if ((cb = es->es_cbhead) == NULL) { (void) splx(s); return; } while (cb && cb->ie_next) { cb->ie_el = 0; cb = (struct iecb *)from_ieoff(es, (ieoff_t)cb->ie_next); } /* start CU */ scb->ie_cbl = to_ieoff(es, (caddr_t)es->es_cbhead); scb->ie_cmd = IECMD_CU_START; ieca(es); (void) splx(s); } /* * Clean up and restart the CBs on the CBL * Called by ieintr at hardware level 3. * Called by iesimple at splie. */ iecbdone(es) register struct ie_softc *es; { iecuclean(es); /* generate more CBs */ iestartout(es - ie_softc); } /* * Do the command simply. * Attempted sleep/wakeup calls, but it refused to work. */ iesimple(es, cb) register struct ie_softc *es; register struct iecb *cb; { register struct iescb *scb = es->es_scb; int s, cmd = 0; es->es_simple++; iedocb(es, cb); iecustart(es); CDELAY(cb->ie_done, IEDELAY); if (!cb->ie_done) { iestat(es); panic("iesimple"); } s = splie(); iechkcca(scb); if (scb->ie_cx) cmd |= IECMD_ACK_CX; if (scb->ie_cnr) cmd |= IECMD_ACK_CNR; scb->ie_cmd = cmd; ieca(es); if (cmd & (IECMD_ACK_CNR|IECMD_ACK_CX)) iecbdone(es); es->es_simple--; (void) splx(s); } /* * Return chip's idea of given address or 0 if not chip accessible */ ieaddr(es, cp) register struct ie_softc *es; caddr_t cp; { int pte; if (es->es_type == IE_OB) { /* onboard ie may only reference obmem */ if ((getkpgmap(cp) & PGT_MASK) != PGT_OBMEM) cp = (caddr_t)0; #ifdef sun3 else if (cp < es->es_memspace) cp = (caddr_t)0; else cp -= (u_long)es->es_memspace; #endif sun3 return ((int)cp); } pte = getkpgmap(cp) & PG_PFNUM; if (pte >= es->es_paddr && pte < es->es_paddr + btoc(es->es_vmemsize)) return ((pte - es->es_paddr) << BSHIFT(0)) + ((int)cp & CLOFSET); return (0); } /* * Check Control Command Acceptance by 82586 */ iechkcca(scb) register struct iescb *scb; { register i; for (i=0; i < IEDELAY; i++) { if (scb->ie_magic != IEMAGIC) panic("ie: scb overwritten"); if (scb->ie_cmd == 0) break; } if (i >= IEDELAY) { printf("ie: cmd not accepted\n"); panic("iechkcca"); } } /* * The control block caching code to bypass rmalloc/rmfree. * It also allows us to check allocated lengths, as well * as aiding instrumentation for verification and tuning. * We have two sizes, small and large. * If the request exceeds the small threshold, we allocate * the larger block. */ #define lo es->es_cbc_lo #define hi es->es_cbc_hi #define cache es->es_cbcache #define ovfl es->es_cbc_ovfl #define small iecbcsmall #define large iecbclarge #define head es->es_cbchain.next #define chain es->es_cbchain /* * Initialize the control block cache */ iecbcinit(es) register struct ie_softc *es; { if (es->es_cbcbusy) iecbcflush(es); lo = -1; hi = CBCACHESIZ; small = sizeof (struct ietbd); large = sizeof (struct ietfd); } /* * cached is a flag used to indicate that the block must be * cached, as it will be freed (for reallocation.) */ long escbget(es, len0, cached) register struct ie_softc *es; int len0; int cached; { long result; int len = rndtoeven(len0); if (!cached) result = (rmalloc(es->es_cbmap, (long)len)); else if (len <= small) if (lo > -1) result = (cache[lo--]); else result = (rmalloc(es->es_cbmap, (long)small)); else if (len <= large) if (hi < CBCACHESIZ) result = (cache[hi++]); else result = (rmalloc(es->es_cbmap, (long)large)); else panic ("escbget"); /* len too large */ if (result == 0 && es->es_cbcbusy) { iecbcflush(es); result = escbget(es, len0, cached); } return (result); } /* * flush the cb cache */ iecbcflush(es) struct ie_softc *es; { printf("WARNING: ie%d: flushing cb cache\n", es - ie_softc); while (lo > -1) rmfree(es->es_cbmap, (long)small, (long)cache[lo--]); while (hi < CBCACHESIZ) rmfree(es->es_cbmap, (long)large, (long)cache[hi++]); es->es_cbcbusy = 0; } long escbput(es, len, ptr) register struct ie_softc *es; int len; long ptr; { int overflow = 0; es->es_cbcbusy = 1; len = rndtoeven(len); if (len <= small) if (lo < hi - 1) cache[++lo] = ptr; else { overflow++; rmfree(es->es_cbmap, (long)small, (long)ptr); } else if (len <= large) if (hi > lo + 1) cache[--hi] = ptr; else { overflow++; rmfree(es->es_cbmap, (long)large, (long)ptr); } else panic("escbput"); if (overflow) { ovfl++; if ((ovfl & OVFLWARNMASK) == 0) { ovfl = 0; printf("ie%d: cache overflowed\n", es - ie_softc); } } } #undef lo #undef hi #undef cache #undef ovfl #undef small #undef large int iefifolim = 12; /* * Set default configuration parameters */ iedefaultconf(ic) register struct ieconf *ic; { bzero((caddr_t)ic, sizeof (struct ieconf)); ic->ieconf_cb.ie_cmd = IE_CONFIG; ic->ieconf_bytes = 12; ic->ieconf_fifolim = iefifolim; ic->ieconf_pream = 2; /* 8 byte preamble */ ic->ieconf_alen = 6; ic->ieconf_acloc = 0; ic->ieconf_space = 96; ic->ieconf_slttmh = 512 >> 8; ic->ieconf_minfrm = 64; ic->ieconf_retry = 15; ic->ieconf_crfilt = 3; } iestat(es) struct ie_softc *es; { register struct iescb *scb = es->es_scb; static char *cus[] = { "idle", "suspended", "ready", "<3>", "<4>", "<5>", "<6>", "<7>" }; static char *rus[] = { "idle", "suspended", "no resources", "<3>", "ready", "<5>", "<6>", "<7>" }; printf("ie%d: scb: ", es - ie_softc); if (scb->ie_cx) printf("cx "); if (scb->ie_fr) printf("fr "); if (scb->ie_cnr) printf("cnr "); if (scb->ie_rnr) printf("rnr "); printf("cus=%s ", cus[scb->ie_cus]); printf("rus=%s\n", rus[scb->ie_rus]); printf("cbl=0x%x rfa=0x%x crc=0x%x aln=0x%x rsc=0x%x ovrn=0x%x\n", scb->ie_cbl, scb->ie_rfa, scb->ie_crcerrs, scb->ie_alnerrs, scb->ie_rscerrs, scb->ie_ovrnerrs); if (scb->ie_cmd) printf("cmd=0x%x\n", scb->ie_cmd & 0xFFFF); } /* * Parity error! Scan entire memory for errors */ ieparity(es) register struct ie_softc *es; { register struct mie_device *mie = es->es_mie; register u_short *s, *e, x; printf("ie%d: parity error src=%d byte=%d addr=%x\n", es-ie_softc, mie->mie_pesrc, mie->mie_pebyte, mie->mie_erraddr); mie->mie_peack = 1; s = (u_short *)es->es_memspace; e = (u_short *)(es->es_memspace + es->es_vmemsize); printf("scanning...\n"); while (s < e) { x = *s; #ifdef lint x = x; #endif if (mie->mie_pe) { printf("off=%x src=%d byte=%d addr=%x\n", (int)s - (int)es->es_memspace, mie->mie_pesrc, mie->mie_pebyte, mie->mie_erraddr); mie->mie_peack = 1; } s++; } printf("done\n"); } /* * Activate the channel attention line */ ieca(es) register struct ie_softc *es; { if (es->es_type == IE_MB) { es->es_mie->mie_ca = 1; es->es_mie->mie_ca = 0; } else { es->es_obie->obie_ca = 1; es->es_obie->obie_ca = 0; } } /* * Wait for an interrupt and relay results */ int iewaitintr(es) register struct ie_softc *es; { register struct obie_device *obie = es->es_obie; register struct mie_device *mie = es->es_mie; int ok; switch (es->es_type) { case IE_OB: CDELAY(obie->obie_intr, IEDELAY); ok = obie->obie_intr; break; case IE_MB: CDELAY(mie->mie_intr, IEDELAY); ok = mie->mie_intr; break; } return (ok); } /* * Cut the chip out of the loop and halt it by starting the reset cycle. * For IE_MB, we must enable pagemaps, hence we complete the reset cycle. */ iechipreset(es) register struct ie_softc *es; { switch (es->es_type) { case IE_MB: es->es_mie->mie_reset = 1; DELAY(IEKLUDGE); /* REQUIRED */ *(char *)&es->es_mie->mie_status = 0; /* power on reset */ break; case IE_OB: *(char *)es->es_obie = 0; /* power on reset */ break; default: panic("iechipreset"); } } /* * Splice the chip into the loop */ iesplice(es) register struct ie_softc *es; { switch (es->es_type) { case IE_MB: es->es_mie->mie_ie = 1; /* enable chip interrupts */ es->es_mie->mie_pie = 1; /* enable parity interrupts */ es->es_mie->mie_noloop = 1; /* enable cable */ break; case IE_OB: es->es_obie->obie_ie = 1; /* enable interrupts */ es->es_obie->obie_noloop = 1; /* enable cable */ break; } } /* * Change 68000 address to Intel 24-bit address. * We take advantage of the fact that all 82586 blocks with 24-bit * addresses have an adjacent unused 8-bit field, and store 32 bits. */ ieaddr_t to_ieaddr(es, cp) struct ie_softc *es; caddr_t cp; { union { int n; char c[4]; } a, b; a.n = ieaddr(es, cp); /* necessary for double mapping */ b.c[0] = a.c[3]; b.c[1] = a.c[2]; b.c[2] = a.c[1]; b.c[3] = 0; return (b.n); } caddr_t from_ieaddr(es, n) struct ie_softc *es; ieaddr_t n; { union { int n; char c[4]; } a, b; a.n = n; b.c[0] = 0; b.c[1] = a.c[2]; b.c[2] = a.c[1]; b.c[3] = a.c[0]; return (es->es_memspace + b.n); } ieoff_t to_ieoff(es, addr) register struct ie_softc *es; caddr_t addr; { union xxx { ieoff_t s; char c[2]; } a, b; a.s = (ieoff_t)(addr - es->es_base); b.c[0] = a.c[1]; b.c[1] = a.c[0]; return (b.s); } caddr_t from_ieoff(es, off) register struct ie_softc *es; ieoff_t off; { union { ieoff_t s; char c[2]; } a, b; a.s = off; b.c[0] = a.c[1]; b.c[1] = a.c[0]; return (es->es_base + b.s); } ieint_t to_ieint(n) ieint_t n; { union { ieint_t s; char c[2]; } a, b; a.s = n; b.c[0] = a.c[1]; b.c[1] = a.c[0]; return (b.s); } #endif NIE > 0