From 15e4fdff9ea6a15ead8496e6aa23022c6fdab734 Mon Sep 17 00:00:00 2001 From: CSRG Date: Thu, 16 Dec 1982 21:15:23 -0800 Subject: [PATCH] BSD 4_1c_2 development Work on file a/sys/vaxif/if_ec.c Work on file a/sys/vaxif/if_en.c Work on file a/sys/vaxif/if_il.c Work on file a/sys/vaxif/if_uba.c Synthesized-from: CSRG/cd1/4.1c.2 --- a/sys/vaxif/if_ec.c | 740 +++++++++++++++++++++++++++++++++++++++++++ a/sys/vaxif/if_en.c | 554 ++++++++++++++++++++++++++++++++ a/sys/vaxif/if_il.c | 598 ++++++++++++++++++++++++++++++++++ a/sys/vaxif/if_uba.c | 251 +++++++++++++++ 4 files changed, 2143 insertions(+) create mode 100644 a/sys/vaxif/if_ec.c create mode 100644 a/sys/vaxif/if_en.c create mode 100644 a/sys/vaxif/if_il.c create mode 100644 a/sys/vaxif/if_uba.c diff --git a/a/sys/vaxif/if_ec.c b/a/sys/vaxif/if_ec.c new file mode 100644 index 0000000000..b063cd2ff5 --- /dev/null +++ b/a/sys/vaxif/if_ec.c @@ -0,0 +1,740 @@ +/* if_ec.c 4.32 82/12/17 */ + +#include "ec.h" + +/* + * 3Com Ethernet Controller interface + */ +#include "../machine/pte.h" + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/mbuf.h" +#include "../h/buf.h" +#include "../h/protosw.h" +#include "../h/socket.h" +#include "../h/vmmac.h" +#include + +#include "../net/if.h" +#include "../net/netisr.h" +#include "../net/route.h" +#include "../netinet/in.h" +#include "../netinet/in_systm.h" +#include "../netinet/ip.h" +#include "../netinet/ip_var.h" +#include "../netpup/pup.h" + +#include "../vax/cpu.h" +#include "../vax/mtpr.h" +#include "../vaxif/if_ether.h" +#include "../vaxif/if_ecreg.h" +#include "../vaxif/if_uba.h" +#include "../vaxuba/ubareg.h" +#include "../vaxuba/ubavar.h" + +#define ECMEM 0000000 + +int ecprobe(), ecattach(), ecrint(), ecxint(), eccollide(); +struct uba_device *ecinfo[NEC]; +u_short ecstd[] = { 0 }; +struct uba_driver ecdriver = + { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo }; +u_char ec_iltop[3] = { 0x02, 0x07, 0x01 }; +u_char ecbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +#define ECUNIT(x) minor(x) + +int ecinit(),ecoutput(),ecreset(); +struct mbuf *ecget(); + +extern struct ifnet loif; + +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * es_if, which the routing code uses to locate the interface. + * This structure contains the output queue for the interface, its address, ... + * We also have, for each interface, a UBA interface structure, which + * contains information about the UNIBUS resources held by the interface: + * map registers, buffered data paths, etc. Information is cached in this + * structure for use by the if_uba.c routines in running the interface + * efficiently. + */ +struct ec_softc { + struct ifnet es_if; /* network-visible interface */ + struct ifuba es_ifuba; /* UNIBUS resources */ + short es_mask; /* mask for current output delay */ + short es_oactive; /* is output active? */ + u_char *es_buf[16]; /* virtual addresses of buffers */ + u_char es_enaddr[6]; /* board's ethernet address */ +} ec_softc[NEC]; + +/* + * Do output DMA to determine interface presence and + * interrupt vector. DMA is too short to disturb other hosts. + */ +ecprobe(reg) + caddr_t reg; +{ + register int br, cvec; /* r11, r10 value-result */ + register struct ecdevice *addr = (struct ecdevice *)reg; + register caddr_t ecbuf = (caddr_t) &umem[numuba][ECMEM]; + +#ifdef lint + br = 0; cvec = br; br = cvec; + ecrint(0); ecxint(0); eccollide(0); +#endif + /* + * Make sure memory is turned on + */ + addr->ec_rcr = EC_AROM; + /* + * Disable map registers for ec unibus space, + * but don't allocate yet. + */ + (void) ubamem(numuba, ECMEM, 32*2, 0); + /* + * Check for existence of buffers on Unibus. + */ + if (badaddr((caddr_t)ecbuf, 2)) { + bad1: + printf("ec: buffer mem not found\n"); + bad2: + (void) ubamem(numuba, 0, 0, 0); /* reenable map (780 only) */ + addr->ec_rcr = EC_MDISAB; /* disable memory */ + return (0); + } +#if VAX780 + if (cpu == VAX_780 && uba_hd[numuba].uh_uba->uba_sr) { + uba_hd[numuba].uh_uba->uba_sr = uba_hd[numuba].uh_uba->uba_sr; + goto bad1; + } +#endif + + /* + * Tell the system that the board has memory here, so it won't + * attempt to allocate the addresses later. + */ + if (ubamem(numuba, ECMEM, 32*2, 1) == 0) { + printf("ecprobe: cannot reserve uba addresses\n"); + goto bad2; + } + + /* + * Make a one byte packet in what should be buffer #0. + * Submit it for sending. This whould cause an xmit interrupt. + * The xmit interrupt vector is 8 bytes after the receive vector, + * so adjust for this before returning. + */ + *(u_short *)ecbuf = (u_short) 03777; + ecbuf[03777] = '\0'; + addr->ec_xcr = EC_XINTEN|EC_XWBN; + DELAY(100000); + addr->ec_xcr = EC_XCLR; + if (cvec > 0 && cvec != 0x200) { + if (cvec & 04) { /* collision interrupt */ + cvec -= 04; + br += 1; /* rcv is collision + 1 */ + } else { /* xmit interrupt */ + cvec -= 010; + br += 2; /* rcv is xmit + 2 */ + } + } + return (1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +ecattach(ui) + struct uba_device *ui; +{ + struct ec_softc *es = &ec_softc[ui->ui_unit]; + register struct ifnet *ifp = &es->es_if; + register struct ecdevice *addr = (struct ecdevice *)ui->ui_addr; + struct sockaddr_in *sin; + int i, j; + u_char *cp; + + ifp->if_unit = ui->ui_unit; + ifp->if_name = "ec"; + ifp->if_mtu = ETHERMTU; + ifp->if_net = ui->ui_flags; + + /* + * Read the ethernet address off the board, one nibble at a time. + */ + addr->ec_xcr = EC_UECLR; + addr->ec_rcr = EC_AROM; + cp = es->es_enaddr; +#define NEXTBIT addr->ec_rcr = EC_AROM|EC_ASTEP; addr->ec_rcr = EC_AROM + for (i=0; i<6; i++) { + *cp = 0; + for (j=0; j<=4; j+=4) { + *cp |= ((addr->ec_rcr >> 8) & 0xf) << j; + NEXTBIT; NEXTBIT; NEXTBIT; NEXTBIT; + } + cp++; + } +#ifdef notdef + printf("ec%d: addr=%x:%x:%x:%x:%x:%x\n", ui->ui_unit, + es->es_enaddr[0]&0xff, es->es_enaddr[1]&0xff, + es->es_enaddr[2]&0xff, es->es_enaddr[3]&0xff, + es->es_enaddr[4]&0xff, es->es_enaddr[5]&0xff); +#endif + ifp->if_host[0] = ((es->es_enaddr[3]&0xff)<<16) | + ((es->es_enaddr[4]&0xff)<<8) | (es->es_enaddr[5]&0xff); + sin = (struct sockaddr_in *)&es->es_if.if_addr; + sin->sin_family = AF_INET; + sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]); + + sin = (struct sockaddr_in *)&ifp->if_broadaddr; + sin->sin_family = AF_INET; + sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); + ifp->if_flags = IFF_BROADCAST; + + ifp->if_init = ecinit; + ifp->if_output = ecoutput; + ifp->if_reset = ecreset; + for (i=0; i<16; i++) + es->es_buf[i] = (u_char *)&umem[ui->ui_ubanum][ECMEM+2048*i]; + if_attach(ifp); +} + +/* + * Reset of interface after UNIBUS reset. + * If interface is on specified uba, reset its state. + */ +ecreset(unit, uban) + int unit, uban; +{ + register struct uba_device *ui; + + if (unit >= NEC || (ui = ecinfo[unit]) == 0 || ui->ui_alive == 0 || + ui->ui_ubanum != uban) + return; + printf(" ec%d", unit); + (void) ubamem(uban, ECMEM, 32*2, 0); /* mr disable (no alloc) */ + ecinit(unit); +} + +/* + * Initialization of interface; clear recorded pending + * operations, and reinitialize UNIBUS usage. + */ +ecinit(unit) + int unit; +{ + struct ec_softc *es = &ec_softc[unit]; + struct ecdevice *addr; + int i, s; + + /* + * Hang receive buffers and start any pending writes. + * Writing into the rcr also makes sure the memory + * is turned on. + */ + addr = (struct ecdevice *)ecinfo[unit]->ui_addr; + s = splimp(); + for (i=ECRHBF; i>=ECRLBF; i--) + addr->ec_rcr = EC_READ|i; + es->es_oactive = 0; + es->es_mask = ~0; + es->es_if.if_flags |= IFF_UP; + if (es->es_if.if_snd.ifq_head) + ecstart(unit); + splx(s); + if_rtinit(&es->es_if, RTF_UP); +} + +/* + * Start or restart output on interface. + * If interface is already active, then this is a retransmit + * after a collision, and just restuff registers. + * If interface is not already active, get another datagram + * to send off of the interface queue, and map it to the interface + * before starting the output. + */ +ecstart(dev) + dev_t dev; +{ + int unit = ECUNIT(dev); + struct ec_softc *es = &ec_softc[unit]; + struct ecdevice *addr; + struct mbuf *m; + + if (es->es_oactive) + goto restart; + + IF_DEQUEUE(&es->es_if.if_snd, m); + if (m == 0) { + es->es_oactive = 0; + return; + } + ecput(es->es_buf[ECTBF], m); + +restart: + addr = (struct ecdevice *)ecinfo[unit]->ui_addr; + addr->ec_xcr = EC_WRITE|ECTBF; + es->es_oactive = 1; +} + +/* + * Ethernet interface transmitter interrupt. + * Start another output if more data to send. + */ +ecxint(unit) + int unit; +{ + register struct ec_softc *es = &ec_softc[unit]; + register struct ecdevice *addr = + (struct ecdevice *)ecinfo[unit]->ui_addr; + + if (es->es_oactive == 0) + return; + if ((addr->ec_xcr&EC_XDONE) == 0 || (addr->ec_xcr&EC_XBN) != ECTBF) { + printf("ec%d: stray xmit interrupt, xcr=%b\n", unit, + addr->ec_xcr, EC_XBITS); + es->es_oactive = 0; + addr->ec_xcr = EC_XCLR; + return; + } + es->es_if.if_opackets++; + es->es_oactive = 0; + es->es_mask = ~0; + addr->ec_xcr = EC_XCLR; + if (es->es_if.if_snd.ifq_head) + ecstart(unit); +} + +/* + * Collision on ethernet interface. Do exponential + * backoff, and retransmit. If have backed off all + * the way print warning diagnostic, and drop packet. + */ +eccollide(unit) + int unit; +{ + struct ec_softc *es = &ec_softc[unit]; + + es->es_if.if_collisions++; + if (es->es_oactive) + ecdocoll(unit); +} + +ecdocoll(unit) + int unit; +{ + register struct ec_softc *es = &ec_softc[unit]; + register struct ecdevice *addr = + (struct ecdevice *)ecinfo[unit]->ui_addr; + register i; + int delay; + + /* + * Es_mask is a 16 bit number with n low zero bits, with + * n the number of backoffs. When es_mask is 0 we have + * backed off 16 times, and give up. + */ + if (es->es_mask == 0) { + es->es_if.if_oerrors++; + printf("ec%d: send error\n", unit); + /* + * Reset interface, then requeue rcv buffers. + * Some incoming packets may be lost, but that + * can't be helped. + */ + addr->ec_xcr = EC_UECLR; + for (i=ECRHBF; i>=ECRLBF; i--) + addr->ec_rcr = EC_READ|i; + /* + * Reset and transmit next packet (if any). + */ + es->es_oactive = 0; + es->es_mask = ~0; + if (es->es_if.if_snd.ifq_head) + ecstart(unit); + return; + } + /* + * Do exponential backoff. Compute delay based on low bits + * of the interval timer. Then delay for that number of + * slot times. A slot time is 51.2 microseconds (rounded to 51). + * This does not take into account the time already used to + * process the interrupt. + */ + es->es_mask <<= 1; + delay = mfpr(ICR) &~ es->es_mask; + DELAY(delay * 51); + /* + * Clear the controller's collision flag, thus enabling retransmit. + */ + addr->ec_xcr = EC_CLEAR; +} + +/* + * Ethernet interface receiver interrupt. + * If input error just drop packet. + * Otherwise purge input buffered data path and examine + * packet to determine type. If can't determine length + * from type, then have to drop packet. Othewise decapsulate + * packet based on type and pass to type specific higher-level + * input routine. + */ +ecrint(unit) + int unit; +{ + struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr; + + while (addr->ec_rcr & EC_RDONE) + ecread(unit); +} + +ecread(unit) + int unit; +{ + register struct ec_softc *es = &ec_softc[unit]; + struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr; + register struct ether_header *ec; + struct mbuf *m; + int len, off, resid, ecoff, rbuf; + register struct ifqueue *inq; + u_char *ecbuf; + + es->es_if.if_ipackets++; + rbuf = addr->ec_rcr & EC_RBN; + if (rbuf < ECRLBF || rbuf > ECRHBF) + panic("ecrint"); + ecbuf = es->es_buf[rbuf]; + ecoff = *(short *)ecbuf; + if (ecoff <= ECRDOFF || ecoff > 2046) { + es->es_if.if_ierrors++; +#ifdef notdef + if (es->es_if.if_ierrors % 100 == 0) + printf("ec%d: += 100 input errors\n", unit); +#endif + goto setup; + } + + /* + * Get input data length. + * Get pointer to ethernet header (in input buffer). + * Deal with trailer protocol: if type is PUP trailer + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + len = ecoff - ECRDOFF - sizeof (struct ether_header); + ec = (struct ether_header *)(ecbuf + ECRDOFF); + ec->ether_type = ntohs((u_short)ec->ether_type); +#define ecdataaddr(ec, off, type) ((type)(((caddr_t)((ec)+1)+(off)))) + if (ec->ether_type >= ETHERPUP_TRAIL && + ec->ether_type < ETHERPUP_TRAIL+ETHERPUP_NTRAILER) { + off = (ec->ether_type - ETHERPUP_TRAIL) * 512; + if (off >= ETHERMTU) + goto setup; /* sanity */ + ec->ether_type = ntohs(*ecdataaddr(ec, off, u_short *)); + resid = ntohs(*(ecdataaddr(ec, off+2, u_short *))); + if (off + resid > len) + goto setup; /* sanity */ + len = off + resid; + } else + off = 0; + if (len == 0) + goto setup; + + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; ecget will then force this header + * information to be at the front, but we still have to drop + * the type and length which are at the front of any trailer data. + */ + m = ecget(ecbuf, len, off); + if (m == 0) + goto setup; + if (off) { + m->m_off += 2 * sizeof (u_short); + m->m_len -= 2 * sizeof (u_short); + } + switch (ec->ether_type) { + +#ifdef INET + case ETHERPUP_IPTYPE: + schednetisr(NETISR_IP); + inq = &ipintrq; + break; +#endif + default: + m_freem(m); + goto setup; + } + + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + goto setup; + } + IF_ENQUEUE(inq, m); + +setup: + /* + * Reset for next packet. + */ + addr->ec_rcr = EC_READ|EC_RCLR|rbuf; +} + +/* + * Ethernet output routine. + * Encapsulate a packet of type family for the local net. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + * If destination is this address or broadcast, send packet to + * loop device to kludge around the fact that 3com interfaces can't + * talk to themselves. + */ +ecoutput(ifp, m0, dst) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; +{ + int type, dest, s, error; + register struct ec_softc *es = &ec_softc[ifp->if_unit]; + register struct mbuf *m = m0; + register struct ether_header *ec; + register int off, i; + struct mbuf *mcopy = (struct mbuf *) 0; /* Null */ + + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; + if ((dest &~ 0xff) == 0) + mcopy = m_copy(m, 0, (int)M_COPYALL); + else if (dest == ((struct sockaddr_in *)&es->es_if.if_addr)-> + sin_addr.s_addr) { + mcopy = m; + goto gotlocal; + } + off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; + if (off > 0 && (off & 0x1ff) == 0 && + m->m_off >= MMINOFF + 2 * sizeof (u_short)) { + type = ETHERPUP_TRAIL + (off>>9); + m->m_off -= 2 * sizeof (u_short); + m->m_len += 2 * sizeof (u_short); + *mtod(m, u_short *) = ntohs((u_short)ETHERPUP_IPTYPE); + *(mtod(m, u_short *) + 1) = ntohs((u_short)m->m_len); + goto gottrailertype; + } + type = ETHERPUP_IPTYPE; + off = 0; + goto gottype; +#endif + + default: + printf("ec%d: can't handle af%d\n", ifp->if_unit, + dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + +gottrailertype: + /* + * Packet to be sent as trailer: move first packet + * (control information) to end of chain. + */ + while (m->m_next) + m = m->m_next; + m->m_next = m0; + m = m0->m_next; + m0->m_next = 0; + m0 = m; + +gottype: + /* + * Add local net header. If no space in first mbuf, + * allocate another. + */ + if (m->m_off > MMAXOFF || + MMINOFF + sizeof (struct ether_header) > m->m_off) { + m = m_get(M_DONTWAIT, MT_HEADER); + if (m == 0) { + error = ENOBUFS; + goto bad; + } + m->m_next = m0; + m->m_off = MMINOFF; + m->m_len = sizeof (struct ether_header); + } else { + m->m_off -= sizeof (struct ether_header); + m->m_len += sizeof (struct ether_header); + } + ec = mtod(m, struct ether_header *); + bcopy((caddr_t)es->es_enaddr, (caddr_t)ec->ether_shost, 6); + if ((dest &~ 0xff) == 0) + bcopy((caddr_t)ecbroadcastaddr, (caddr_t)ec->ether_dhost, 6); + else { + u_char *to = dest & 0x8000 ? ec_iltop : es->es_enaddr; + + bcopy((caddr_t)to, (caddr_t)ec->ether_dhost, 3); + ec->ether_dhost[3] = (dest>>8) & 0x7f; + ec->ether_dhost[4] = (dest>>16) & 0xff; + ec->ether_dhost[5] = (dest>>24) & 0xff; + } + ec->ether_type = htons((u_short)type); + + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + s = splimp(); + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + error = ENOBUFS; + goto qfull; + } + IF_ENQUEUE(&ifp->if_snd, m); + if (es->es_oactive == 0) + ecstart(ifp->if_unit); + splx(s); + +gotlocal: + return(mcopy ? looutput(&loif, mcopy, dst) : 0); + +qfull: + m0 = m; + splx(s); +bad: + m_freem(m0); + return(error); +} + +/* + * Routine to copy from mbuf chain to transmit + * buffer in UNIBUS memory. + * If packet size is less than the minimum legal size, + * the buffer is expanded. We probably should zero out the extra + * bytes for security, but that would slow things down. + */ +ecput(ecbuf, m) + u_char *ecbuf; + struct mbuf *m; +{ + register struct mbuf *mp; + register int off; + u_char *bp; + + for (off = 2048, mp = m; mp; mp = mp->m_next) + off -= mp->m_len; + if (2048 - off < ETHERMIN + sizeof (struct ether_header)) + off = 2048 - ETHERMIN - sizeof (struct ether_header); + *(u_short *)ecbuf = off; + bp = (u_char *)(ecbuf + off); + for (mp = m; mp; mp = mp->m_next) { + register unsigned len = mp->m_len; + u_char *mcp; + + if (len == 0) + continue; + mcp = mtod(mp, u_char *); + if ((unsigned)bp & 01) { + *bp++ = *mcp++; + len--; + } + if (off = (len >> 1)) { + register u_short *to, *from; + + to = (u_short *)bp; + from = (u_short *)mcp; + do + *to++ = *from++; + while (--off > 0); + bp = (u_char *)to, + mcp = (u_char *)from; + } + if (len & 01) + *bp++ = *mcp++; + } + m_freem(m); +} + +/* + * Routine to copy from UNIBUS memory into mbufs. + * Similar in spirit to if_rubaget. + * + * Warning: This makes the fairly safe assumption that + * mbufs have even lengths. + */ +struct mbuf * +ecget(ecbuf, totlen, off0) + u_char *ecbuf; + int totlen, off0; +{ + register struct mbuf *m; + struct mbuf *top = 0, **mp = ⊤ + register int off = off0, len; + u_char *cp; + + cp = ecbuf + ECRDOFF + sizeof (struct ether_header); + while (totlen > 0) { + register int words; + u_char *mcp; + + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto bad; + if (off) { + len = totlen - off; + cp = ecbuf + ECRDOFF + + sizeof (struct ether_header) + off; + } else + len = totlen; + if (len >= CLBYTES) { + struct mbuf *p; + + MCLGET(p, 1); + if (p != 0) { + m->m_len = len = CLBYTES; + m->m_off = (int)p - (int)m; + } else { + m->m_len = len = MIN(MLEN, len); + m->m_off = MMINOFF; + } + } else { + m->m_len = len = MIN(MLEN, len); + m->m_off = MMINOFF; + } + mcp = mtod(m, u_char *); + if (words = (len >> 1)) { + register u_short *to, *from; + + to = (u_short *)mcp; + from = (u_short *)cp; + do + *to++ = *from++; + while (--words > 0); + mcp = (u_char *)to; + cp = (u_char *)from; + } + if (len & 01) + *mcp++ = *cp++; + *mp = m; + mp = &m->m_next; + if (off == 0) { + totlen -= len; + continue; + } + off += len; + if (off == totlen) { + cp = ecbuf + ECRDOFF + sizeof (struct ether_header); + off = 0; + totlen = off0; + } + } + return (top); +bad: + m_freem(top); + return (0); +} diff --git a/a/sys/vaxif/if_en.c b/a/sys/vaxif/if_en.c new file mode 100644 index 0000000000..c860a0fa53 --- /dev/null +++ b/a/sys/vaxif/if_en.c @@ -0,0 +1,554 @@ +/* if_en.c 4.75 82/12/17 */ + +#include "en.h" + +/* + * Xerox prototype (3 Mb) Ethernet interface driver. + */ +#include "../machine/pte.h" + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/mbuf.h" +#include "../h/buf.h" +#include "../h/protosw.h" +#include "../h/socket.h" +#include "../h/vmmac.h" +#include + +#include "../net/if.h" +#include "../net/netisr.h" +#include "../net/route.h" +#include "../netinet/in.h" +#include "../netinet/in_systm.h" +#include "../netinet/ip.h" +#include "../netinet/ip_var.h" +#include "../netpup/pup.h" + +#include "../vax/cpu.h" +#include "../vax/mtpr.h" +#include "../vaxif/if_en.h" +#include "../vaxif/if_enreg.h" +#include "../vaxif/if_uba.h" +#include "../vaxuba/ubareg.h" +#include "../vaxuba/ubavar.h" + +#define ENMTU (1024+512) +#define ENMRU (1024+512+16) /* 16 is enough to receive trailer */ + +int enprobe(), enattach(), enrint(), enxint(), encollide(); +struct uba_device *eninfo[NEN]; +u_short enstd[] = { 0 }; +struct uba_driver endriver = + { enprobe, 0, enattach, 0, enstd, "en", eninfo }; +#define ENUNIT(x) minor(x) + +int eninit(),enoutput(),enreset(); + +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * es_if, which the routing code uses to locate the interface. + * This structure contains the output queue for the interface, its address, ... + * We also have, for each interface, a UBA interface structure, which + * contains information about the UNIBUS resources held by the interface: + * map registers, buffered data paths, etc. Information is cached in this + * structure for use by the if_uba.c routines in running the interface + * efficiently. + */ +struct en_softc { + struct ifnet es_if; /* network-visible interface */ + struct ifuba es_ifuba; /* UNIBUS resources */ + short es_delay; /* current output delay */ + short es_mask; /* mask for current output delay */ + short es_lastx; /* host last transmitted to */ + short es_oactive; /* is output active? */ + short es_olen; /* length of last output */ +} en_softc[NEN]; + +/* + * Do output DMA to determine interface presence and + * interrupt vector. DMA is too short to disturb other hosts. + */ +enprobe(reg) + caddr_t reg; +{ + register int br, cvec; /* r11, r10 value-result */ + register struct endevice *addr = (struct endevice *)reg; + +#ifdef lint + br = 0; cvec = br; br = cvec; + enrint(0); enxint(0); encollide(0); +#endif + addr->en_istat = 0; + addr->en_owc = -1; + addr->en_oba = 0; + addr->en_ostat = EN_IEN|EN_GO; + DELAY(100000); + addr->en_ostat = 0; +#ifdef ECHACK + br = 0x16; +#endif + return (1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +enattach(ui) + struct uba_device *ui; +{ + register struct en_softc *es = &en_softc[ui->ui_unit]; + register struct sockaddr_in *sin; + + es->es_if.if_unit = ui->ui_unit; + es->es_if.if_name = "en"; + es->es_if.if_mtu = ENMTU; + es->es_if.if_net = ui->ui_flags; + es->es_if.if_host[0] = + (~(((struct endevice *)eninfo[ui->ui_unit]->ui_addr)->en_addr)) & 0xff; + sin = (struct sockaddr_in *)&es->es_if.if_addr; + sin->sin_family = AF_INET; + sin->sin_addr = if_makeaddr(es->es_if.if_net, es->es_if.if_host[0]); + sin = (struct sockaddr_in *)&es->es_if.if_broadaddr; + sin->sin_family = AF_INET; + sin->sin_addr = if_makeaddr(es->es_if.if_net, 0); + es->es_if.if_flags = IFF_BROADCAST; + es->es_if.if_init = eninit; + es->es_if.if_output = enoutput; + es->es_if.if_reset = enreset; + es->es_ifuba.ifu_flags = UBA_NEEDBDP | UBA_NEED16 | UBA_CANTWAIT; +#if defined(VAX750) + /* don't chew up 750 bdp's */ + if (cpu == VAX_750 && ui->ui_unit > 0) + es->es_ifuba.ifu_flags &= ~UBA_NEEDBDP; +#endif + if_attach(&es->es_if); +} + +/* + * Reset of interface after UNIBUS reset. + * If interface is on specified uba, reset its state. + */ +enreset(unit, uban) + int unit, uban; +{ + register struct uba_device *ui; + + if (unit >= NEN || (ui = eninfo[unit]) == 0 || ui->ui_alive == 0 || + ui->ui_ubanum != uban) + return; + printf(" en%d", unit); + eninit(unit); +} + +/* + * Initialization of interface; clear recorded pending + * operations, and reinitialize UNIBUS usage. + */ +eninit(unit) + int unit; +{ + register struct en_softc *es = &en_softc[unit]; + register struct uba_device *ui = eninfo[unit]; + register struct endevice *addr; + int s; + + if (if_ubainit(&es->es_ifuba, ui->ui_ubanum, + sizeof (struct en_header), (int)btoc(ENMRU)) == 0) { + printf("en%d: can't initialize\n", unit); + es->es_if.if_flags &= ~IFF_UP; + return; + } + addr = (struct endevice *)ui->ui_addr; + addr->en_istat = addr->en_ostat = 0; + + /* + * Hang a receive and start any + * pending writes by faking a transmit complete. + */ + s = splimp(); + addr->en_iba = es->es_ifuba.ifu_r.ifrw_info; + addr->en_iwc = -(sizeof (struct en_header) + ENMRU) >> 1; + addr->en_istat = EN_IEN|EN_GO; + es->es_oactive = 1; + es->es_if.if_flags |= IFF_UP; + enxint(unit); + splx(s); + if_rtinit(&es->es_if, RTF_UP); +} + +int enalldelay = 0; +int enlastdel = 50; +int enlastmask = (~0) << 5; + +/* + * Start or restart output on interface. + * If interface is already active, then this is a retransmit + * after a collision, and just restuff registers and delay. + * If interface is not already active, get another datagram + * to send off of the interface queue, and map it to the interface + * before starting the output. + */ +enstart(dev) + dev_t dev; +{ + int unit = ENUNIT(dev); + struct uba_device *ui = eninfo[unit]; + register struct en_softc *es = &en_softc[unit]; + register struct endevice *addr; + struct mbuf *m; + int dest; + + if (es->es_oactive) + goto restart; + + /* + * Not already active: dequeue another request + * and map it to the UNIBUS. If no more requests, + * just return. + */ + IF_DEQUEUE(&es->es_if.if_snd, m); + if (m == 0) { + es->es_oactive = 0; + return; + } + dest = mtod(m, struct en_header *)->en_dhost; + es->es_olen = if_wubaput(&es->es_ifuba, m); + + /* + * Ethernet cannot take back-to-back packets (no + * buffering in interface. To help avoid overrunning + * receivers, enforce a small delay (about 1ms) in interface: + * * between all packets when enalldelay + * * whenever last packet was broadcast + * * whenever this packet is to same host as last packet + */ + if (enalldelay || es->es_lastx == 0 || es->es_lastx == dest) { + es->es_delay = enlastdel; + es->es_mask = enlastmask; + } + es->es_lastx = dest; + +restart: + /* + * Have request mapped to UNIBUS for transmission. + * Purge any stale data from this BDP, and start the otput. + */ + if (es->es_ifuba.ifu_flags & UBA_NEEDBDP) + UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_w.ifrw_bdp); + addr = (struct endevice *)ui->ui_addr; + addr->en_oba = (int)es->es_ifuba.ifu_w.ifrw_info; + addr->en_odelay = es->es_delay; + addr->en_owc = -((es->es_olen + 1) >> 1); + addr->en_ostat = EN_IEN|EN_GO; + es->es_oactive = 1; +} + +/* + * Ethernet interface transmitter interrupt. + * Start another output if more data to send. + */ +enxint(unit) + int unit; +{ + register struct uba_device *ui = eninfo[unit]; + register struct en_softc *es = &en_softc[unit]; + register struct endevice *addr = (struct endevice *)ui->ui_addr; + + if (es->es_oactive == 0) + return; + if (es->es_mask && (addr->en_ostat&EN_OERROR)) { + es->es_if.if_oerrors++; + endocoll(unit); + return; + } + es->es_if.if_opackets++; + es->es_oactive = 0; + es->es_delay = 0; + es->es_mask = ~0; + if (es->es_ifuba.ifu_xtofree) { + m_freem(es->es_ifuba.ifu_xtofree); + es->es_ifuba.ifu_xtofree = 0; + } + if (es->es_if.if_snd.ifq_head == 0) { + es->es_lastx = 256; /* putatively illegal */ + return; + } + enstart(unit); +} + +/* + * Collision on ethernet interface. Do exponential + * backoff, and retransmit. If have backed off all + * the way print warning diagnostic, and drop packet. + */ +encollide(unit) + int unit; +{ + struct en_softc *es = &en_softc[unit]; + + es->es_if.if_collisions++; + if (es->es_oactive == 0) + return; + endocoll(unit); +} + +endocoll(unit) + int unit; +{ + register struct en_softc *es = &en_softc[unit]; + + /* + * Es_mask is a 16 bit number with n low zero bits, with + * n the number of backoffs. When es_mask is 0 we have + * backed off 16 times, and give up. + */ + if (es->es_mask == 0) { + printf("en%d: send error\n", unit); + enxint(unit); + return; + } + /* + * Another backoff. Restart with delay based on n low bits + * of the interval timer. + */ + es->es_mask <<= 1; + es->es_delay = mfpr(ICR) &~ es->es_mask; + enstart(unit); +} + +struct sockaddr_pup pupsrc = { AF_PUP }; +struct sockaddr_pup pupdst = { AF_PUP }; +struct sockproto pupproto = { PF_PUP }; +/* + * Ethernet interface receiver interrupt. + * If input error just drop packet. + * Otherwise purge input buffered data path and examine + * packet to determine type. If can't determine length + * from type, then have to drop packet. Othewise decapsulate + * packet based on type and pass to type specific higher-level + * input routine. + */ +enrint(unit) + int unit; +{ + register struct en_softc *es = &en_softc[unit]; + struct endevice *addr = (struct endevice *)eninfo[unit]->ui_addr; + register struct en_header *en; + struct mbuf *m; + int len; short resid; + register struct ifqueue *inq; + int off; + + es->es_if.if_ipackets++; + + /* + * Purge BDP; drop if input error indicated. + */ + if (es->es_ifuba.ifu_flags & UBA_NEEDBDP) + UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_r.ifrw_bdp); + if (addr->en_istat&EN_IERROR) { + es->es_if.if_ierrors++; + goto setup; + } + + /* + * Calculate input data length. + * Get pointer to ethernet header (in input buffer). + * Deal with trailer protocol: if type is PUP trailer + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + resid = addr->en_iwc; + if (resid) + resid |= 0176000; + len = (((sizeof (struct en_header) + ENMRU) >> 1) + resid) << 1; + len -= sizeof (struct en_header); + if (len > ENMRU) + goto setup; /* sanity */ + en = (struct en_header *)(es->es_ifuba.ifu_r.ifrw_addr); +#define endataaddr(en, off, type) ((type)(((caddr_t)((en)+1)+(off)))) + if (en->en_type >= ENPUP_TRAIL && + en->en_type < ENPUP_TRAIL+ENPUP_NTRAILER) { + off = (en->en_type - ENPUP_TRAIL) * 512; + if (off > ENMTU) + goto setup; /* sanity */ + en->en_type = *endataaddr(en, off, u_short *); + resid = *(endataaddr(en, off+2, u_short *)); + if (off + resid > len) + goto setup; /* sanity */ + len = off + resid; + } else + off = 0; + if (len == 0) + goto setup; + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; if_rubaget will then force this header + * information to be at the front, but we still have to drop + * the type and length which are at the front of any trailer data. + */ + m = if_rubaget(&es->es_ifuba, len, off); + if (m == 0) + goto setup; + if (off) { + m->m_off += 2 * sizeof (u_short); + m->m_len -= 2 * sizeof (u_short); + } + switch (en->en_type) { + +#ifdef INET + case ENPUP_IPTYPE: + schednetisr(NETISR_IP); + inq = &ipintrq; + break; +#endif +#ifdef PUP + case ENPUP_PUPTYPE: { + struct pup_header *pup = mtod(m, struct pup_header *); + + pupproto.sp_protocol = pup->pup_type; + pupdst.spup_addr = pup->pup_dport; + pupsrc.spup_addr = pup->pup_sport; + raw_input(m, &pupproto, (struct sockaddr *)&pupsrc, + (struct sockaddr *)&pupdst); + goto setup; + } +#endif + default: + m_freem(m); + goto setup; + } + + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + } else + IF_ENQUEUE(inq, m); + +setup: + /* + * Reset for next packet. + */ + addr->en_iba = es->es_ifuba.ifu_r.ifrw_info; + addr->en_iwc = -(sizeof (struct en_header) + ENMRU) >> 1; + addr->en_istat = EN_IEN|EN_GO; +} + +/* + * Ethernet output routine. + * Encapsulate a packet of type family for the local net. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + */ +enoutput(ifp, m0, dst) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; +{ + int type, dest, s, error; + register struct mbuf *m = m0; + register struct en_header *en; + register int off; + + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; + if (in_lnaof(*((struct in_addr *)&dest)) >= 0x100) { + error = EPERM; /* ??? */ + goto bad; + } + dest = (dest >> 24) & 0xff; + off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; + if (off > 0 && (off & 0x1ff) == 0 && + m->m_off >= MMINOFF + 2 * sizeof (u_short)) { + type = ENPUP_TRAIL + (off>>9); + m->m_off -= 2 * sizeof (u_short); + m->m_len += 2 * sizeof (u_short); + *mtod(m, u_short *) = ENPUP_IPTYPE; + *(mtod(m, u_short *) + 1) = m->m_len; + goto gottrailertype; + } + type = ENPUP_IPTYPE; + off = 0; + goto gottype; +#endif +#ifdef PUP + case AF_PUP: + dest = ((struct sockaddr_pup *)dst)->spup_addr.pp_host; + type = ENPUP_PUPTYPE; + off = 0; + goto gottype; +#endif + + default: + printf("en%d: can't handle af%d\n", ifp->if_unit, + dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + +gottrailertype: + /* + * Packet to be sent as trailer: move first packet + * (control information) to end of chain. + */ + while (m->m_next) + m = m->m_next; + m->m_next = m0; + m = m0->m_next; + m0->m_next = 0; + m0 = m; + +gottype: + /* + * Add local net header. If no space in first mbuf, + * allocate another. + */ + if (m->m_off > MMAXOFF || + MMINOFF + sizeof (struct en_header) > m->m_off) { + m = m_get(M_DONTWAIT, MT_HEADER); + if (m == 0) { + error = ENOBUFS; + goto bad; + } + m->m_next = m0; + m->m_off = MMINOFF; + m->m_len = sizeof (struct en_header); + } else { + m->m_off -= sizeof (struct en_header); + m->m_len += sizeof (struct en_header); + } + en = mtod(m, struct en_header *); + en->en_shost = ifp->if_host[0]; + en->en_dhost = dest; + en->en_type = type; + + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + s = splimp(); + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + error = ENOBUFS; + goto qfull; + } + IF_ENQUEUE(&ifp->if_snd, m); + if (en_softc[ifp->if_unit].es_oactive == 0) + enstart(ifp->if_unit); + splx(s); + return (0); +qfull: + m0 = m; + splx(s); +bad: + m_freem(m0); + return (error); +} diff --git a/a/sys/vaxif/if_il.c b/a/sys/vaxif/if_il.c new file mode 100644 index 0000000000..677401c58f --- /dev/null +++ b/a/sys/vaxif/if_il.c @@ -0,0 +1,598 @@ +/* if_il.c 4.17 82/12/17 */ + +#include "il.h" + +/* + * Interlan Ethernet Communications Controller interface + */ +#include "../machine/pte.h" + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/mbuf.h" +#include "../h/buf.h" +#include "../h/protosw.h" +#include "../h/socket.h" +#include "../h/vmmac.h" +#include + +#include "../net/if.h" +#include "../net/netisr.h" +#include "../net/route.h" +#include "../netinet/in.h" +#include "../netinet/in_systm.h" +#include "../netinet/ip.h" +#include "../netinet/ip_var.h" +#include "../netpup/pup.h" + +#include "../vax/cpu.h" +#include "../vax/mtpr.h" +#include "../vaxif/if_ether.h" +#include "../vaxif/if_il.h" +#include "../vaxif/if_ilreg.h" +#include "../vaxif/if_uba.h" +#include "../vaxuba/ubareg.h" +#include "../vaxuba/ubavar.h" + +int ilprobe(), ilattach(), ilrint(), ilcint(); +struct uba_device *ilinfo[NIL]; +u_short ilstd[] = { 0 }; +struct uba_driver ildriver = + { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; +#define ILUNIT(x) minor(x) +int ilinit(),iloutput(),ilreset(),ilwatch(); + +u_char il_ectop[3] = { 0x02, 0x60, 0x8c }; +u_char ilbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * is_if, which the routing code uses to locate the interface. + * This structure contains the output queue for the interface, its address, ... + * We also have, for each interface, a UBA interface structure, which + * contains information about the UNIBUS resources held by the interface: + * map registers, buffered data paths, etc. Information is cached in this + * structure for use by the if_uba.c routines in running the interface + * efficiently. + */ +struct il_softc { + struct ifnet is_if; /* network-visible interface */ + struct ifuba is_ifuba; /* UNIBUS resources */ + int is_flags; +#define ILF_OACTIVE 0x1 /* output is active */ +#define ILF_RCVPENDING 0x2 /* start rcv in ilcint */ +#define ILF_STATPENDING 0x4 /* stat cmd pending */ + short is_lastcmd; /* can't read csr, so must save it */ + short is_scaninterval; /* interval of stat collection */ +#define ILWATCHINTERVAL 60 /* once every 60 seconds */ + struct il_stats is_stats; /* holds on-board statistics */ + struct il_stats is_sum; /* summation over time */ + int is_ubaddr; /* mapping registers of is_stats */ +} il_softc[NIL]; + +ilprobe(reg) + caddr_t reg; +{ + register int br, cvec; /* r11, r10 value-result */ + register struct ildevice *addr = (struct ildevice *)reg; + register i; + +#ifdef lint + br = 0; cvec = br; br = cvec; + i = 0; ilrint(i); ilcint(i); ilwatch(i); +#endif + + addr->il_csr = ILC_OFFLINE|IL_CIE; + DELAY(100000); + i = addr->il_csr; /* clear CDONE */ + if (cvec > 0 && cvec != 0x200) + cvec -= 4; + return (1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. A STATUS command is done to get the ethernet + * address and other interesting data. + */ +ilattach(ui) + struct uba_device *ui; +{ + register struct il_softc *is = &il_softc[ui->ui_unit]; + register struct ifnet *ifp = &is->is_if; + register struct ildevice *addr = (struct ildevice *)ui->ui_addr; + struct sockaddr_in *sin; + + ifp->if_unit = ui->ui_unit; + ifp->if_name = "il"; + ifp->if_mtu = ETHERMTU; + ifp->if_net = ui->ui_flags; + + /* + * Reset the board and map the statistics + * buffer onto the Unibus. + */ + addr->il_csr = ILC_RESET; + while ((addr->il_csr&IL_CDONE) == 0) + ; + if (addr->il_csr&IL_STATUS) + printf("il%d: reset failed, csr=%b\n", ui->ui_unit, + addr->il_csr, IL_BITS); + + is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, + sizeof (struct il_stats), 0); + addr->il_bar = is->is_ubaddr & 0xffff; + addr->il_bcr = sizeof (struct il_stats); + addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; + while ((addr->il_csr&IL_CDONE) == 0) + ; + if (addr->il_csr&IL_STATUS) + printf("il%d: status failed, csr=%b\n", ui->ui_unit, + addr->il_csr, IL_BITS); + ubarelse(ui->ui_ubanum, &is->is_ubaddr); + printf("il%d: addr=%x:%x:%x:%x:%x:%x module=%s firmware=%s\n", + ui->ui_unit, + is->is_stats.ils_addr[0]&0xff, is->is_stats.ils_addr[1]&0xff, + is->is_stats.ils_addr[2]&0xff, is->is_stats.ils_addr[3]&0xff, + is->is_stats.ils_addr[4]&0xff, is->is_stats.ils_addr[5]&0xff, + is->is_stats.ils_module, is->is_stats.ils_firmware); + ifp->if_host[0] = + ((is->is_stats.ils_addr[3]&0xff)<<16) | 0x800000 | + ((is->is_stats.ils_addr[4]&0xff)<<8) | + (is->is_stats.ils_addr[5]&0xff); + sin = (struct sockaddr_in *)&ifp->if_addr; + sin->sin_family = AF_INET; + sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]); + + sin = (struct sockaddr_in *)&ifp->if_broadaddr; + sin->sin_family = AF_INET; + sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); + ifp->if_flags = IFF_BROADCAST; + + ifp->if_init = ilinit; + ifp->if_output = iloutput; + ifp->if_reset = ilreset; + ifp->if_watchdog = ilwatch; + is->is_scaninterval = ILWATCHINTERVAL; + ifp->if_timer = is->is_scaninterval; + is->is_ifuba.ifu_flags = UBA_CANTWAIT; +#ifdef notdef + is->is_ifuba.ifu_flags |= UBA_NEEDBDP; +#endif + if_attach(ifp); +} + +/* + * Reset of interface after UNIBUS reset. + * If interface is on specified uba, reset its state. + */ +ilreset(unit, uban) + int unit, uban; +{ + register struct uba_device *ui; + + if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || + ui->ui_ubanum != uban) + return; + printf(" il%d", unit); + ilinit(unit); +} + +/* + * Initialization of interface; clear recorded pending + * operations, and reinitialize UNIBUS usage. + */ +ilinit(unit) + int unit; +{ + register struct il_softc *is = &il_softc[unit]; + register struct uba_device *ui = ilinfo[unit]; + register struct ildevice *addr; + int s; + + if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, + sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) { + printf("il%d: can't initialize\n", unit); + is->is_if.if_flags &= ~IFF_UP; + return; + } + is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, + sizeof (struct il_stats), 0); + addr = (struct ildevice *)ui->ui_addr; + + /* + * Turn off source address insertion (it's faster this way), + * and set board online. + */ + s = splimp(); + addr->il_csr = ILC_CISA; + while ((addr->il_csr & IL_CDONE) == 0) + ; + addr->il_csr = ILC_ONLINE; + while ((addr->il_csr & IL_CDONE) == 0) + ; + /* + * Set board online. + * Hang receive buffer and start any pending + * writes by faking a transmit complete. + * Receive bcr is not a muliple of 4 so buffer + * chaining can't happen. + */ + s = splimp(); + addr->il_csr = ILC_ONLINE; + while ((addr->il_csr & IL_CDONE) == 0) + ; + addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; + addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; + addr->il_csr = + ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; + while ((addr->il_csr & IL_CDONE) == 0) + ; + is->is_flags = ILF_OACTIVE; + is->is_if.if_flags |= IFF_UP; + is->is_lastcmd = 0; + ilcint(unit); + splx(s); + if_rtinit(&is->is_if, RTF_UP); +} + +/* + * Start output on interface. + * Get another datagram to send off of the interface queue, + * and map it to the interface before starting the output. + */ +ilstart(dev) + dev_t dev; +{ + int unit = ILUNIT(dev), len; + struct uba_device *ui = ilinfo[unit]; + register struct il_softc *is = &il_softc[unit]; + register struct ildevice *addr; + struct mbuf *m; + short csr; + + IF_DEQUEUE(&is->is_if.if_snd, m); + addr = (struct ildevice *)ui->ui_addr; + if (m == 0) { + if ((is->is_flags & ILF_STATPENDING) == 0) + return; + addr->il_bar = is->is_ubaddr & 0xffff; + addr->il_bcr = sizeof (struct il_stats); + csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE; + is->is_flags &= ~ILF_STATPENDING; + goto startcmd; + } + len = if_wubaput(&is->is_ifuba, m); + /* + * Ensure minimum packet length. + * This makes the safe assumtion that there are no virtual holes + * after the data. + * For security, it might be wise to zero out the added bytes, + * but we're mainly interested in speed at the moment. + */ + if (len - sizeof(struct ether_header) < ETHERMIN) + len = ETHERMIN + sizeof(struct ether_header); + if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) + UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); + addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; + addr->il_bcr = len; + csr = + ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE; + +startcmd: + is->is_lastcmd = csr & IL_CMD; + addr->il_csr = csr; + is->is_flags |= ILF_OACTIVE; +} + +/* + * Command done interrupt. + */ +ilcint(unit) + int unit; +{ + register struct il_softc *is = &il_softc[unit]; + struct uba_device *ui = ilinfo[unit]; + register struct ildevice *addr = (struct ildevice *)ui->ui_addr; + short csr; + + if ((is->is_flags & ILF_OACTIVE) == 0) { + printf("il%d: stray xmit interrupt, csr=%b\n", unit, + addr->il_csr, IL_BITS); + return; + } + + csr = addr->il_csr; + /* + * Hang receive buffer if it couldn't + * be done earlier (in ilrint). + */ + if (is->is_flags & ILF_RCVPENDING) { + addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; + addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; + addr->il_csr = + ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; + while ((addr->il_csr & IL_CDONE) == 0) + ; + is->is_flags &= ~ILF_RCVPENDING; + } + is->is_flags &= ~ILF_OACTIVE; + csr &= IL_STATUS; + switch (is->is_lastcmd) { + + case ILC_XMIT: + is->is_if.if_opackets++; + if (csr > ILERR_RETRIES) + is->is_if.if_oerrors++; + break; + + case ILC_STAT: + if (csr == ILERR_SUCCESS) + iltotal(is); + break; + } + if (is->is_ifuba.ifu_xtofree) { + m_freem(is->is_ifuba.ifu_xtofree); + is->is_ifuba.ifu_xtofree = 0; + } + ilstart(unit); +} + +/* + * Ethernet interface receiver interrupt. + * If input error just drop packet. + * Otherwise purge input buffered data path and examine + * packet to determine type. If can't determine length + * from type, then have to drop packet. Othewise decapsulate + * packet based on type and pass to type specific higher-level + * input routine. + */ +ilrint(unit) + int unit; +{ + register struct il_softc *is = &il_softc[unit]; + struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; + register struct il_rheader *il; + struct mbuf *m; + int len, off, resid; + register struct ifqueue *inq; + + is->is_if.if_ipackets++; + if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) + UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); + il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); + len = il->ilr_length - sizeof(struct il_rheader); + if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 || + len > ETHERMTU) { + is->is_if.if_ierrors++; +#ifdef notdef + if (is->is_if.if_ierrors % 100 == 0) + printf("il%d: += 100 input errors\n", unit); +#endif + goto setup; + } + + /* + * Deal with trailer protocol: if type is PUP trailer + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + il->ilr_type = ntohs((u_short)il->ilr_type); +#define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) + if (il->ilr_type >= ETHERPUP_TRAIL && + il->ilr_type < ETHERPUP_TRAIL+ETHERPUP_NTRAILER) { + off = (il->ilr_type - ETHERPUP_TRAIL) * 512; + if (off >= ETHERMTU) + goto setup; /* sanity */ + il->ilr_type = ntohs(*ildataaddr(il, off, u_short *)); + resid = ntohs(*(ildataaddr(il, off+2, u_short *))); + if (off + resid > len) + goto setup; /* sanity */ + len = off + resid; + } else + off = 0; + if (len == 0) + goto setup; + + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; ilget will then force this header + * information to be at the front, but we still have to drop + * the type and length which are at the front of any trailer data. + */ + m = if_rubaget(&is->is_ifuba, len, off); + if (m == 0) + goto setup; + if (off) { + m->m_off += 2 * sizeof (u_short); + m->m_len -= 2 * sizeof (u_short); + } + switch (il->ilr_type) { + +#ifdef INET + case ETHERPUP_IPTYPE: + schednetisr(NETISR_IP); + inq = &ipintrq; + break; +#endif + default: + m_freem(m); + goto setup; + } + + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + goto setup; + } + IF_ENQUEUE(inq, m); + +setup: + /* + * Reset for next packet if possible. + * If waiting for transmit command completion, set flag + * and wait until command completes. + */ + if (is->is_flags & ILF_OACTIVE) { + is->is_flags |= ILF_RCVPENDING; + return; + } + addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; + addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; + addr->il_csr = + ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; + while ((addr->il_csr & IL_CDONE) == 0) + ; +} + +/* + * Ethernet output routine. + * Encapsulate a packet of type family for the local net. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + */ +iloutput(ifp, m0, dst) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; +{ + int type, dest, s, error; + register struct il_softc *is = &il_softc[ifp->if_unit]; + register struct mbuf *m = m0; + register struct ether_header *il; + register int off; + + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; + off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; + if (off > 0 && (off & 0x1ff) == 0 && + m->m_off >= MMINOFF + 2 * sizeof (u_short)) { + type = ETHERPUP_TRAIL + (off>>9); + m->m_off -= 2 * sizeof (u_short); + m->m_len += 2 * sizeof (u_short); + *mtod(m, u_short *) = htons((u_short)ETHERPUP_IPTYPE); + *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); + goto gottrailertype; + } + type = ETHERPUP_IPTYPE; + off = 0; + goto gottype; +#endif + + default: + printf("il%d: can't handle af%d\n", ifp->if_unit, + dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + +gottrailertype: + /* + * Packet to be sent as trailer: move first packet + * (control information) to end of chain. + */ + while (m->m_next) + m = m->m_next; + m->m_next = m0; + m = m0->m_next; + m0->m_next = 0; + m0 = m; + +gottype: + /* + * Add local net header. If no space in first mbuf, + * allocate another. + */ + if (m->m_off > MMAXOFF || + MMINOFF + sizeof (struct ether_header) > m->m_off) { + m = m_get(M_DONTWAIT, MT_HEADER); + if (m == 0) { + error = ENOBUFS; + goto bad; + } + m->m_next = m0; + m->m_off = MMINOFF; + m->m_len = sizeof (struct ether_header); + } else { + m->m_off -= sizeof (struct ether_header); + m->m_len += sizeof (struct ether_header); + } + il = mtod(m, struct ether_header *); + if ((dest &~ 0xff) == 0) + bcopy((caddr_t)ilbroadcastaddr, (caddr_t)il->ether_dhost, 6); + else { + u_char *to = dest & 0x8000 ? is->is_stats.ils_addr : il_ectop; + + bcopy((caddr_t)to, (caddr_t)il->ether_dhost, 3); + il->ether_dhost[3] = (dest>>8) & 0x7f; + il->ether_dhost[4] = (dest>>16) & 0xff; + il->ether_dhost[5] = (dest>>24) & 0xff; + } + bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)il->ether_shost, 6); + il->ether_type = htons((u_short)type); + + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + s = splimp(); + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + splx(s); + m_freem(m); + return (ENOBUFS); + } + IF_ENQUEUE(&ifp->if_snd, m); + if ((is->is_flags & ILF_OACTIVE) == 0) + ilstart(ifp->if_unit); + splx(s); + return (0); + +bad: + m_freem(m0); + return (error); +} + +/* + * Watchdog routine, request statistics from board. + */ +ilwatch(unit) + int unit; +{ + register struct il_softc *is = &il_softc[unit]; + register struct ifnet *ifp = &is->is_if; + int s; + + if (is->is_flags & ILF_STATPENDING) { + ifp->if_timer = is->is_scaninterval; + return; + } + s = splimp(); + is->is_flags |= ILF_STATPENDING; + if ((is->is_flags & ILF_OACTIVE) == 0) + ilstart(ifp->if_unit); + splx(s); + ifp->if_timer = is->is_scaninterval; +} + +/* + * Total up the on-board statistics. + */ +iltotal(is) + register struct il_softc *is; +{ + register u_short *interval, *sum, *end; + + interval = &is->is_stats.ils_frames; + sum = &is->is_sum.ils_frames; + end = is->is_sum.ils_fill2; + while (sum < end) + *sum++ += *interval++; + is->is_if.if_collisions = is->is_sum.ils_collis; +} diff --git a/a/sys/vaxif/if_uba.c b/a/sys/vaxif/if_uba.c new file mode 100644 index 0000000000..e7ce8d67a7 --- /dev/null +++ b/a/sys/vaxif/if_uba.c @@ -0,0 +1,251 @@ +/* if_uba.c 4.16 82/12/17 */ + +#include "../machine/pte.h" + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/mbuf.h" +#include "../h/map.h" +#include "../h/buf.h" +#include "../h/cmap.h" +#include "../h/vmmac.h" +#include "../h/socket.h" + +#include "../net/if.h" + +#include "../vax/mtpr.h" +#include "../vaxif/if_uba.h" +#include "../vaxuba/ubareg.h" +#include "../vaxuba/ubavar.h" + +/* + * Routines supporting UNIBUS network interfaces. + * + * TODO: + * Support interfaces using only one BDP statically. + */ + +/* + * Init UNIBUS for interface on uban whose headers of size hlen are to + * end on a page boundary. We allocate a UNIBUS map register for the page + * with the header, and nmr more UNIBUS map registers for i/o on the adapter, + * doing this twice: once for reading and once for writing. We also + * allocate page frames in the mbuffer pool for these pages. + */ +if_ubainit(ifu, uban, hlen, nmr) + register struct ifuba *ifu; + int uban, hlen, nmr; +{ + register caddr_t cp; + int i, ncl; + + ncl = clrnd(nmr + CLSIZE) / CLSIZE; + if (ifu->ifu_r.ifrw_addr) + cp = ifu->ifu_r.ifrw_addr - (CLBYTES - hlen); + else { + cp = m_clalloc(2 * ncl, MPG_SPACE); + if (cp == 0) + return (0); + ifu->ifu_r.ifrw_addr = cp + CLBYTES - hlen; + ifu->ifu_w.ifrw_addr = ifu->ifu_r.ifrw_addr + ncl * CLBYTES; + ifu->ifu_hlen = hlen; + ifu->ifu_uban = uban; + ifu->ifu_uba = uba_hd[uban].uh_uba; + } + if (if_ubaalloc(ifu, &ifu->ifu_r, nmr) == 0) + goto bad; + if (if_ubaalloc(ifu, &ifu->ifu_w, nmr) == 0) + goto bad2; + for (i = 0; i < nmr; i++) + ifu->ifu_wmap[i] = ifu->ifu_w.ifrw_mr[i]; + ifu->ifu_xswapd = 0; + return (1); +bad2: + ubarelse(ifu->ifu_uban, &ifu->ifu_r.ifrw_info); +bad: + m_pgfree(cp, 2 * ncl); + ifu->ifu_r.ifrw_addr = 0; + return (0); +} + +/* + * Setup either a ifrw structure by allocating UNIBUS map registers, + * possibly a buffered data path, and initializing the fields of + * the ifrw structure to minimize run-time overhead. + */ +static +if_ubaalloc(ifu, ifrw, nmr) + struct ifuba *ifu; + register struct ifrw *ifrw; + int nmr; +{ + register int info; + + info = + uballoc(ifu->ifu_uban, ifrw->ifrw_addr, nmr*NBPG + ifu->ifu_hlen, + ifu->ifu_flags); + if (info == 0) + return (0); + ifrw->ifrw_info = info; + ifrw->ifrw_bdp = UBAI_BDP(info); + ifrw->ifrw_proto = UBAMR_MRV | (UBAI_BDP(info) << UBAMR_DPSHIFT); + ifrw->ifrw_mr = &ifu->ifu_uba->uba_map[UBAI_MR(info) + 1]; + return (1); +} + +/* + * Pull read data off a interface. + * Len is length of data, with local net header stripped. + * Off is non-zero if a trailer protocol was used, and + * gives the offset of the trailer information. + * We copy the trailer information and then all the normal + * data into mbufs. When full cluster sized units are present + * on the interface on cluster boundaries we can get them more + * easily by remapping, and take advantage of this here. + */ +struct mbuf * +if_rubaget(ifu, totlen, off0) + register struct ifuba *ifu; + int totlen, off0; +{ + struct mbuf *top, **mp, *m; + int off = off0, len; + register caddr_t cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen; + + + top = 0; + mp = ⊤ + while (totlen > 0) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto bad; + if (off) { + len = totlen - off; + cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen + off; + } else + len = totlen; + if (len >= CLBYTES) { + struct mbuf *p; + struct pte *cpte, *ppte; + int x, *ip, i; + + MCLGET(p, 1); + if (p == 0) + goto nopage; + len = m->m_len = CLBYTES; + m->m_off = (int)p - (int)m; + if (!claligned(cp)) + goto copy; + + /* + * Switch pages mapped to UNIBUS with new page p, + * as quick form of copy. Remap UNIBUS and invalidate. + */ + cpte = &Mbmap[mtocl(cp)*CLSIZE]; + ppte = &Mbmap[mtocl(p)*CLSIZE]; + x = btop(cp - ifu->ifu_r.ifrw_addr); + ip = (int *)&ifu->ifu_r.ifrw_mr[x]; + for (i = 0; i < CLSIZE; i++) { + struct pte t; + t = *ppte; *ppte++ = *cpte; *cpte = t; + *ip++ = + cpte++->pg_pfnum|ifu->ifu_r.ifrw_proto; + mtpr(TBIS, cp); + cp += NBPG; + mtpr(TBIS, (caddr_t)p); + p += NBPG / sizeof (*p); + } + goto nocopy; + } +nopage: + m->m_len = MIN(MLEN, len); + m->m_off = MMINOFF; +copy: + bcopy(cp, mtod(m, caddr_t), (unsigned)m->m_len); + cp += m->m_len; +nocopy: + *mp = m; + mp = &m->m_next; + if (off) { + /* sort of an ALGOL-W style for statement... */ + off += m->m_len; + if (off == totlen) { + cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen; + off = 0; + totlen = off0; + } + } else + totlen -= m->m_len; + } + return (top); +bad: + m_freem(top); + return (0); +} + +/* + * Map a chain of mbufs onto a network interface + * in preparation for an i/o operation. + * The argument chain of mbufs includes the local network + * header which is copied to be in the mapped, aligned + * i/o space. + */ +if_wubaput(ifu, m) + register struct ifuba *ifu; + register struct mbuf *m; +{ + register struct mbuf *mp; + register caddr_t cp, dp; + register int i; + int xswapd = 0; + int x, cc; + + cp = ifu->ifu_w.ifrw_addr; + while (m) { + dp = mtod(m, char *); + if (claligned(cp) && claligned(dp) && m->m_len == CLBYTES) { + struct pte *pte; int *ip; + pte = &Mbmap[mtocl(dp)*CLSIZE]; + x = btop(cp - ifu->ifu_w.ifrw_addr); + ip = (int *)&ifu->ifu_w.ifrw_mr[x]; + for (i = 0; i < CLSIZE; i++) + *ip++ = + ifu->ifu_w.ifrw_proto | pte++->pg_pfnum; + xswapd |= 1 << (x>>(CLSHIFT-PGSHIFT)); + mp = m->m_next; + m->m_next = ifu->ifu_xtofree; + ifu->ifu_xtofree = m; + cp += m->m_len; + } else { + bcopy(mtod(m, caddr_t), cp, (unsigned)m->m_len); + cp += m->m_len; + MFREE(m, mp); + } + m = mp; + } + + /* + * Xswapd is the set of clusters we just mapped out. Ifu->ifu_xswapd + * is the set of clusters mapped out from before. We compute + * the number of clusters involved in this operation in x. + * Clusters mapped out before and involved in this operation + * should be unmapped so original pages will be accessed by the device. + */ + cc = cp - ifu->ifu_w.ifrw_addr; + x = ((cc - ifu->ifu_hlen) + CLBYTES - 1) >> CLSHIFT; + ifu->ifu_xswapd &= ~xswapd; + xswapd &= ~ifu->ifu_xswapd; + while (i = ffs(ifu->ifu_xswapd)) { + i--; + if (i >= x) + break; + ifu->ifu_xswapd &= ~(1<ifu_w.ifrw_mr[i] = ifu->ifu_wmap[i]; + i++; + } + } + ifu->ifu_xswapd |= xswapd; + return (cc); +} -- 2.20.1