From e7692f90a475f387e7fef4b7bc4a5bbd6466f1aa Mon Sep 17 00:00:00 2001 From: Lars Brinkhoff Date: Tue, 24 Jan 2017 12:57:48 +0100 Subject: [PATCH] 4.2BSD driver for DEUNA. For reference. --- doc/if_de.c | 988 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/if_dereg.h | 183 +++++++++ 2 files changed, 1171 insertions(+) create mode 100644 doc/if_de.c create mode 100644 doc/if_dereg.h diff --git a/doc/if_de.c b/doc/if_de.c new file mode 100644 index 00000000..686bb809 --- /dev/null +++ b/doc/if_de.c @@ -0,0 +1,988 @@ +#ifdef RCSIDENT +static char *rcsident = "$Header: if_de.c,v 1.1 84/02/01 17:18:51 mike Exp $"; +#endif + +#include "de.h" +#if NDE > 0 + +/* + * DEC DEUNA interface + * + * Lou Salkind + * New York University + * + * TODO: + * timeout routine (get statistics) + */ +#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 "../h/ioctl.h" +#include "../h/errno.h" + +#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 "../netinet/if_ether.h" +#include "../netpup/pup.h" + +#include "../vax/cpu.h" +#include "../vax/mtpr.h" +#include "../vaxif/if_dereg.h" +#include "../vaxif/if_uba.h" +#include "../vaxuba/ubareg.h" +#include "../vaxuba/ubavar.h" + +#define NXMT 2 /* number of transmit buffers */ +#define NRCV 4 /* number of receive buffers (must be > 1) */ +#define NTOT (NXMT + NRCV) + +int dedebug = 0; + +int deprobe(), deattach(), deintr(); +struct uba_device *deinfo[NDE]; +u_short destd[] = { 0 }; +struct uba_driver dedriver = + { deprobe, 0, deattach, 0, destd, "de", deinfo }; +#define DEUNIT(x) minor(x) +int deinit(),deoutput(),deioctl(),dereset(); +struct mbuf *deget(); + + +/* + * The following generalizes the ifuba structure + * to an arbitrary number of receive and transmit + * buffers. + */ +struct deuba { + short ifu_uban; /* uba number */ + short ifu_hlen; /* local net header length */ + struct uba_regs *ifu_uba; /* uba regs, in vm */ + struct ifrw ifu_r[NRCV]; /* receive information */ + struct ifrw ifu_w[NXMT]; /* transmit information */ + /* these should only be pointers */ + short ifu_flags; /* used during uballoc's */ +}; + +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * ds_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 de_softc { + struct arpcom ds_ac; /* Ethernet common part */ +#define ds_if ds_ac.ac_if /* network-visible interface */ +#define ds_addr ds_ac.ac_enaddr /* hardware Ethernet address */ + int ds_flags; +#define DSF_LOCK 1 /* lock out destart */ +#define DSF_RUNNING 2 + int ds_ubaddr; /* map info for incore structs */ + struct deuba ds_deuba; /* unibus resource structure */ + /* the following structures are always mapped in */ + struct de_pcbb ds_pcbb; /* port control block */ + struct de_ring ds_xrent[NXMT]; /* transmit ring entrys */ + struct de_ring ds_rrent[NRCV]; /* receive ring entrys */ + struct de_udbbuf ds_udbbuf; /* UNIBUS data buffer */ + /* end mapped area */ +#define INCORE_BASE(p) ((char *)&(p)->ds_pcbb) +#define RVAL_OFF(n) ((char *)&de_softc[0].n - INCORE_BASE(&de_softc[0])) +#define LVAL_OFF(n) ((char *)de_softc[0].n - INCORE_BASE(&de_softc[0])) +#define PCBB_OFFSET RVAL_OFF(ds_pcbb) +#define XRENT_OFFSET LVAL_OFF(ds_xrent) +#define RRENT_OFFSET LVAL_OFF(ds_rrent) +#define UDBBUF_OFFSET RVAL_OFF(ds_udbbuf) +#define INCORE_SIZE RVAL_OFF(ds_xindex) + int ds_xindex; /* UNA index into transmit chain */ + int ds_rindex; /* UNA index into receive chain */ + int ds_xfree; /* index for next transmit buffer */ + int ds_nxmit; /* # of transmits in progress */ +} de_softc[NDE]; + +deprobe(reg) + caddr_t reg; +{ + register int br, cvec; /* r11, r10 value-result */ + register struct dedevice *addr = (struct dedevice *)reg; + register i; + +#ifdef lint + br = 0; cvec = br; br = cvec; + i = 0; derint(i); deintr(i); +#endif + + addr->pcsr0 = PCSR0_RSET; + while ((addr->pcsr0 & PCSR0_INTR) == 0) + ; + /* make board interrupt by executing a GETPCBB command */ + addr->pcsr0 = PCSR0_INTE; + addr->pcsr2 = 0; + addr->pcsr3 = 0; + addr->pcsr0 = PCSR0_INTE|CMD_GETPCBB; + DELAY(100000); + return(1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. We get the ethernet address here. + */ +deattach(ui) + struct uba_device *ui; +{ + register struct de_softc *ds = &de_softc[ui->ui_unit]; + register struct ifnet *ifp = &ds->ds_if; + register struct dedevice *addr = (struct dedevice *)ui->ui_addr; + struct sockaddr_in *sin; + int csr0; + + ifp->if_unit = ui->ui_unit; + ifp->if_name = "de"; + ifp->if_mtu = ETHERMTU; + + /* + * Reset the board and temporarily map + * the pcbb buffer onto the Unibus. + */ + addr->pcsr0 = PCSR0_RSET; + while ((addr->pcsr0 & PCSR0_INTR) == 0) + ; + csr0 = addr->pcsr0; + addr->pchigh = csr0 >> 8; + if (csr0 & PCSR0_PCEI) + printf("de%d: reset failed, csr0=%b csr1=%b\n", ui->ui_unit, + csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); + ds->ds_ubaddr = uballoc(ui->ui_ubanum, (char *)&ds->ds_pcbb, + sizeof (struct de_pcbb), 0); + addr->pcsr2 = ds->ds_ubaddr & 0xffff; + addr->pcsr3 = (ds->ds_ubaddr >> 16) & 0x3; + addr->pclow = CMD_GETPCBB; + while ((addr->pcsr0 & PCSR0_INTR) == 0) + ; + csr0 = addr->pcsr0; + addr->pchigh = csr0 >> 8; + if (csr0 & PCSR0_PCEI) + printf("de%d: pcbb failed, csr0=%b csr1=%b\n", ui->ui_unit, + csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); + ds->ds_pcbb.pcbb0 = FC_RDPHYAD; + addr->pclow = CMD_GETCMD; + while ((addr->pcsr0 & PCSR0_INTR) == 0) + ; + csr0 = addr->pcsr0; + addr->pchigh = csr0 >> 8; + if (csr0 & PCSR0_PCEI) + printf("de%d: rdphyad failed, csr0=%b csr1=%b\n", ui->ui_unit, + csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); + ubarelse(ui->ui_ubanum, &ds->ds_ubaddr); + if (dedebug) + printf("de%d: addr=%d:%d:%d:%d:%d:%d\n", ui->ui_unit, + ds->ds_pcbb.pcbb2&0xff, (ds->ds_pcbb.pcbb2>>8)&0xff, + ds->ds_pcbb.pcbb4&0xff, (ds->ds_pcbb.pcbb4>>8)&0xff, + ds->ds_pcbb.pcbb6&0xff, (ds->ds_pcbb.pcbb6>>8)&0xff); + bcopy((caddr_t)&ds->ds_pcbb.pcbb2, (caddr_t)ds->ds_addr, + sizeof (ds->ds_addr)); + sin = (struct sockaddr_in *)&ifp->if_addr; + sin->sin_family = AF_INET; + sin->sin_addr = arpmyaddr((struct arpcom *)0); + ifp->if_init = deinit; + ifp->if_output = deoutput; + ifp->if_ioctl = deioctl; + ifp->if_reset = dereset; + ds->ds_deuba.ifu_flags = UBA_CANTWAIT; +#ifdef notdef + /* CAN WE USE BDP's ??? */ + ds->ds_deuba.ifu_flags |= UBA_NEEDBDP; +#endif + if_attach(ifp); +} + +/* + * Reset of interface after UNIBUS reset. + * If interface is on specified uba, reset its state. + */ +dereset(unit, uban) + int unit, uban; +{ + register struct uba_device *ui; + + if (unit >= NDE || (ui = deinfo[unit]) == 0 || ui->ui_alive == 0 || + ui->ui_ubanum != uban) + return; + printf(" de%d", unit); + deinit(unit); +} + +/* + * Initialization of interface; clear recorded pending + * operations, and reinitialize UNIBUS usage. + */ +deinit(unit) + int unit; +{ + register struct de_softc *ds = &de_softc[unit]; + register struct uba_device *ui = deinfo[unit]; + register struct dedevice *addr; + register struct ifrw *ifrw; + int s; + register struct ifnet *ifp = &ds->ds_if; + register struct sockaddr_in *sin; + struct de_ring *rp; + int incaddr; + int csr0; + + sin = (struct sockaddr_in *)&ifp->if_addr; + if (sin->sin_addr.s_addr == 0) /* if address still unknown */ + return; + + if (ifp->if_flags & IFF_RUNNING) + goto justarp; + if (de_ubainit(&ds->ds_deuba, ui->ui_ubanum, + sizeof (struct ether_header), (int)btoc(ETHERMTU)) == 0) { + printf("de%d: can't initialize\n", unit); + ds->ds_if.if_flags &= ~IFF_UP; + return; + } + ds->ds_ubaddr = uballoc(ui->ui_ubanum, INCORE_BASE(ds), INCORE_SIZE,0); + addr = (struct dedevice *)ui->ui_addr; + + /* set the pcbb block address */ + incaddr = ds->ds_ubaddr + PCBB_OFFSET; + addr->pcsr2 = incaddr & 0xffff; + addr->pcsr3 = (incaddr >> 16) & 0x3; + addr->pclow = CMD_GETPCBB; + while ((addr->pcsr0 & PCSR0_INTR) == 0) + ; + csr0 = addr->pcsr0; + addr->pchigh = csr0 >> 8; + if (csr0 & PCSR0_PCEI) + printf("de%d: pcbb failed, csr0=%b csr1=%b\n", ui->ui_unit, + csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); + + /* set the transmit and receive ring header addresses */ + incaddr = ds->ds_ubaddr + UDBBUF_OFFSET; + ds->ds_pcbb.pcbb0 = FC_WTRING; + ds->ds_pcbb.pcbb2 = incaddr & 0xffff; + ds->ds_pcbb.pcbb4 = (incaddr >> 16) & 0x3; + + incaddr = ds->ds_ubaddr + XRENT_OFFSET; + ds->ds_udbbuf.b_tdrbl = incaddr & 0xffff; + ds->ds_udbbuf.b_tdrbh = (incaddr >> 16) & 0x3; + ds->ds_udbbuf.b_telen = sizeof (struct de_ring) / sizeof (short); + ds->ds_udbbuf.b_trlen = NXMT; + incaddr = ds->ds_ubaddr + RRENT_OFFSET; + ds->ds_udbbuf.b_rdrbl = incaddr & 0xffff; + ds->ds_udbbuf.b_rdrbh = (incaddr >> 16) & 0x3; + ds->ds_udbbuf.b_relen = sizeof (struct de_ring) / sizeof (short); + ds->ds_udbbuf.b_rrlen = NRCV; + + addr->pclow = CMD_GETCMD; + while ((addr->pcsr0 & PCSR0_INTR) == 0) + ; + csr0 = addr->pcsr0; + addr->pchigh = csr0 >> 8; + if (csr0 & PCSR0_PCEI) + printf("de%d: wtring failed, csr0=%b csr1=%b\n", ui->ui_unit, + csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); + + /* initialize the mode - enable hardware padding */ + ds->ds_pcbb.pcbb0 = FC_WTMODE; + /* let hardware do padding - set MTCH bit on broadcast */ + ds->ds_pcbb.pcbb2 = MOD_TPAD|MOD_HDX; + addr->pclow = CMD_GETCMD; + while ((addr->pcsr0 & PCSR0_INTR) == 0) + ; + csr0 = addr->pcsr0; + addr->pchigh = csr0 >> 8; + if (csr0 & PCSR0_PCEI) + printf("de%d: wtmode failed, csr0=%b csr1=%b\n", ui->ui_unit, + csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); + + /* set up the receive and transmit ring entries */ + ifrw = &ds->ds_deuba.ifu_w[0]; + for (rp = &ds->ds_xrent[0]; rp < &ds->ds_xrent[NXMT]; rp++) { + rp->r_segbl = ifrw->ifrw_info & 0xffff; + rp->r_segbh = (ifrw->ifrw_info >> 16) & 0x3; + rp->r_flags = 0; + ifrw++; + } + ifrw = &ds->ds_deuba.ifu_r[0]; + for (rp = &ds->ds_rrent[0]; rp < &ds->ds_rrent[NRCV]; rp++) { + rp->r_slen = sizeof (struct de_buf); + rp->r_segbl = ifrw->ifrw_info & 0xffff; + rp->r_segbh = (ifrw->ifrw_info >> 16) & 0x3; + rp->r_flags = RFLG_OWN; /* hang receive */ + ifrw++; + } + + /* start up the board (rah rah) */ + s = splimp(); + ds->ds_rindex = ds->ds_xindex = ds->ds_xfree = 0; + ds->ds_if.if_flags |= IFF_UP|IFF_RUNNING; + destart(unit); /* queue output packets */ + addr->pclow = PCSR0_INTE; /* avoid interlock */ + addr->pclow = CMD_START | PCSR0_INTE; + ds->ds_flags |= DSF_RUNNING; + splx(s); +justarp: + if_rtinit(&ds->ds_if, RTF_UP); + arpattach(&ds->ds_ac); + arpwhohas(&ds->ds_ac, &sin->sin_addr); +} + +/* + * Setup output on interface. + * Get another datagram to send off of the interface queue, + * and map it to the interface before starting the output. + */ +destart(unit) + int unit; +{ + int len; + struct uba_device *ui = deinfo[unit]; + struct dedevice *addr = (struct dedevice *)ui->ui_addr; + register struct de_softc *ds = &de_softc[unit]; + register struct de_ring *rp; + struct mbuf *m; + register int nxmit; + + /* + * the following test is necessary, since + * the code is not reentrant and we have + * multiple transmission buffers. + */ + if (ds->ds_flags & DSF_LOCK) + return; + for (nxmit = ds->ds_nxmit; nxmit < NXMT; nxmit++) { + IF_DEQUEUE(&ds->ds_if.if_snd, m); + if (m == 0) + break; + rp = &ds->ds_xrent[ds->ds_xfree]; + if (rp->r_flags & XFLG_OWN) + panic("deuna xmit in progress"); + len = deput(&ds->ds_deuba.ifu_w[ds->ds_xfree], m); + if (ds->ds_deuba.ifu_flags & UBA_NEEDBDP) + UBAPURGE(ds->ds_deuba.ifu_uba, + ds->ds_deuba.ifu_w[ds->ds_xfree].ifrw_bdp); + rp->r_slen = len; + rp->r_tdrerr = 0; + rp->r_flags = XFLG_STP|XFLG_ENP|XFLG_OWN; + + ds->ds_xfree++; + if (ds->ds_xfree == NXMT) + ds->ds_xfree = 0; + } + if (ds->ds_nxmit != nxmit) { + ds->ds_nxmit = nxmit; + if (ds->ds_flags & DSF_RUNNING) + addr->pclow = PCSR0_INTE|CMD_PDMD; + } +} + +/* + * Command done interrupt. + */ +deintr(unit) + int unit; +{ + struct uba_device *ui = deinfo[unit]; + register struct dedevice *addr = (struct dedevice *)ui->ui_addr; + register struct de_softc *ds = &de_softc[unit]; + register struct de_ring *rp; + short csr0; + + /* save flags right away - clear out interrupt bits */ + csr0 = addr->pcsr0; + addr->pchigh = csr0 >> 8; + + + ds->ds_flags |= DSF_LOCK; /* prevent entering destart */ + /* + * if receive, put receive buffer on mbuf + * and hang the request again + */ + derecv(unit); + + /* + * Poll transmit ring and check status. + * Be careful about loopback requests. + * Then free buffer space and check for + * more transmit requests. + */ + for ( ; ds->ds_nxmit > 0; ds->ds_nxmit--) { + rp = &ds->ds_xrent[ds->ds_xindex]; + if (rp->r_flags & XFLG_OWN) + break; + ds->ds_if.if_opackets++; + /* check for unusual conditions */ + if (rp->r_flags & (XFLG_ERRS|XFLG_MTCH|XFLG_ONE|XFLG_MORE)) { + if (rp->r_flags & XFLG_ERRS) { + /* output error */ + ds->ds_if.if_oerrors++; + if (dedebug) + printf("de%d: oerror, flags=%b tdrerr=%b (len=%d)\n", + unit, rp->r_flags, XFLG_BITS, + rp->r_tdrerr, XERR_BITS, rp->r_slen); + } else if (rp->r_flags & XFLG_ONE) { + /* one collision */ + ds->ds_if.if_collisions++; + } else if (rp->r_flags & XFLG_MORE) { + /* more than one collision */ + ds->ds_if.if_collisions += 2; /* guess */ + } else if (rp->r_flags & XFLG_MTCH) { + /* received our own packet */ + ds->ds_if.if_ipackets++; + deread(ds, &ds->ds_deuba.ifu_w[ds->ds_xindex], + rp->r_slen - sizeof (struct ether_header)); + } + } + /* check if next transmit buffer also finished */ + ds->ds_xindex++; + if (ds->ds_xindex == NXMT) + ds->ds_xindex = 0; + } + ds->ds_flags &= ~DSF_LOCK; + destart(unit); + + if (csr0 & PCSR0_RCBI) { + printf("de%d: buffer unavailable\n", unit); + addr->pclow = PCSR0_INTE|CMD_PDMD; + } +} + +/* + * Ethernet interface receiver interface. + * 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. + */ +derecv(unit) + int unit; +{ + register struct de_softc *ds = &de_softc[unit]; + register struct de_ring *rp; + int len; + + rp = &ds->ds_rrent[ds->ds_rindex]; + while ((rp->r_flags & RFLG_OWN) == 0) { + ds->ds_if.if_ipackets++; + if (ds->ds_deuba.ifu_flags & UBA_NEEDBDP) + UBAPURGE(ds->ds_deuba.ifu_uba, + ds->ds_deuba.ifu_r[ds->ds_rindex].ifrw_bdp); + len = (rp->r_lenerr&RERR_MLEN) - sizeof (struct ether_header) + - 4; /* don't forget checksum! */ + /* check for errors */ + if ((rp->r_flags & (RFLG_ERRS|RFLG_FRAM|RFLG_OFLO|RFLG_CRC)) || + (rp->r_flags&(RFLG_STP|RFLG_ENP)) != (RFLG_STP|RFLG_ENP) || + (rp->r_lenerr & (RERR_BUFL|RERR_UBTO|RERR_NCHN)) || + len < ETHERMIN || len > ETHERMTU) { + ds->ds_if.if_ierrors++; + if (dedebug) + printf("de%d: ierror, flags=%b lenerr=%b (len=%d)\n", + unit, rp->r_flags, RFLG_BITS, rp->r_lenerr, + RERR_BITS, len); + } else + deread(ds, &ds->ds_deuba.ifu_r[ds->ds_rindex], len); + + /* hang the receive buffer again */ + rp->r_lenerr = 0; + rp->r_flags = RFLG_OWN; + + /* check next receive buffer */ + ds->ds_rindex++; + if (ds->ds_rindex == NRCV) + ds->ds_rindex = 0; + rp = &ds->ds_rrent[ds->ds_rindex]; + } +} + +/* + * Pass a packet to the higher levels. + * We deal with the trailer protocol here. + */ +deread(ds, ifrw, len) + register struct de_softc *ds; + struct ifrw *ifrw; + int len; +{ + struct ether_header *eh; + struct mbuf *m; + int off, resid; + register struct ifqueue *inq; + + /* + * 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. + */ + eh = (struct ether_header *)ifrw->ifrw_addr; + eh->ether_type = ntohs((u_short)eh->ether_type); +#define dedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) + if (eh->ether_type >= ETHERPUP_TRAIL && + eh->ether_type < ETHERPUP_TRAIL+ETHERPUP_NTRAILER) { + off = (eh->ether_type - ETHERPUP_TRAIL) * 512; + if (off >= ETHERMTU) + return; /* sanity */ + eh->ether_type = ntohs(*dedataaddr(eh, off, u_short *)); + resid = ntohs(*(dedataaddr(eh, off+2, u_short *))); + if (off + resid > len) + return; /* sanity */ + len = off + resid; + } else + off = 0; + if (len == 0) + return; + + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; deget 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 = deget(&ds->ds_deuba, ifrw, len, off); + if (m == 0) + return; + if (off) { + m->m_off += 2 * sizeof (u_short); + m->m_len -= 2 * sizeof (u_short); + } + switch (eh->ether_type) { + +#ifdef INET + case ETHERPUP_IPTYPE: + schednetisr(NETISR_IP); + inq = &ipintrq; + break; + + case ETHERPUP_ARPTYPE: + arpinput(&ds->ds_ac, m); + return; +#endif + default: + m_freem(m); + return; + } + + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + return; + } + IF_ENQUEUE(inq, m); +} + +/* + * 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. + */ +deoutput(ifp, m0, dst) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; +{ + int type, s, error; + u_char edst[6]; + struct in_addr idst; + register struct de_softc *ds = &de_softc[ifp->if_unit]; + register struct mbuf *m = m0; + register struct ether_header *eh; + register int off; + + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + idst = ((struct sockaddr_in *)dst)->sin_addr; + if (!arpresolve(&ds->ds_ac, m, &idst, edst)) + return (0); /* if not yet resolved */ + off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; + /* need per host negotiation */ + if ((ifp->if_flags & IFF_NOTRAILERS) == 0) + 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 + + case AF_UNSPEC: + eh = (struct ether_header *)dst->sa_data; + bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst)); + type = eh->ether_type; + goto gottype; + + default: + printf("de%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); + } + eh = mtod(m, struct ether_header *); + eh->ether_type = htons((u_short)type); + bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); + /* DEUNA fills in source address */ + + /* + * 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); + destart(ifp->if_unit); + splx(s); + return (0); + +bad: + m_freem(m0); + return (error); +} + +/* + * Routines supporting UNIBUS network interfaces. + */ + +/* + * 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 for each receive and transmit buffer. We also + * allocate page frames in the mbuffer pool for these pages. + */ +de_ubainit(ifu, uban, hlen, nmr) + register struct deuba *ifu; + int uban, hlen, nmr; +{ + register caddr_t cp, dp; + register struct ifrw *ifrw; + int ncl; + + ncl = clrnd(nmr + CLSIZE) / CLSIZE; + if (ifu->ifu_r[0].ifrw_addr) + /* + * If the first read buffer has a non-zero + * address, it means we have already allocated core + */ + cp = ifu->ifu_r[0].ifrw_addr - (CLBYTES - hlen); + else { + cp = m_clalloc(NTOT * ncl, MPG_SPACE); + if (cp == 0) + return (0); + ifu->ifu_hlen = hlen; + ifu->ifu_uban = uban; + ifu->ifu_uba = uba_hd[uban].uh_uba; + dp = cp + CLBYTES - hlen; + for (ifrw = ifu->ifu_r; ifrw < &ifu->ifu_r[NRCV]; ifrw++) { + ifrw->ifrw_addr = dp; + dp += ncl * CLBYTES; + } + for (ifrw = ifu->ifu_w; ifrw < &ifu->ifu_w[NXMT]; ifrw++) { + ifrw->ifrw_addr = dp; + dp += ncl * CLBYTES; + } + } + /* allocate for receive ring */ + for (ifrw = ifu->ifu_r; ifrw < &ifu->ifu_r[NRCV]; ifrw++) { + if (de_ubaalloc(ifu, ifrw, nmr) == 0) { + struct ifrw *if2; + + for (if2 = ifu->ifu_r; if2 < ifrw; if2++) + ubarelse(ifu->ifu_uban, &if2->ifrw_info); + goto bad; + } + } + /* and now transmit ring */ + for (ifrw = ifu->ifu_w; ifrw < &ifu->ifu_w[NXMT]; ifrw++) { + if (de_ubaalloc(ifu, ifrw, nmr) == 0) { + struct ifrw *if2; + + for (if2 = ifu->ifu_w; if2 < ifrw; if2++) + ubarelse(ifu->ifu_uban, &if2->ifrw_info); + for (if2 = ifu->ifu_r; if2 < &ifu->ifu_r[NRCV]; if2++) + ubarelse(ifu->ifu_uban, &if2->ifrw_info); + goto bad; + } + } + return (1); +bad: + m_pgfree(cp, NTOT * ncl); + ifu->ifu_r[0].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 +de_ubaalloc(ifu, ifrw, nmr) + struct deuba *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 * +deget(ifu, ifrw, totlen, off0) + register struct deuba *ifu; + register struct ifrw *ifrw; + int totlen, off0; +{ + struct mbuf *top, **mp, *m; + int off = off0, len; + register caddr_t cp = ifrw->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 = ifrw->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 - ifrw->ifrw_addr); + ip = (int *)&ifrw->ifrw_mr[x]; + for (i = 0; i < CLSIZE; i++) { + struct pte t; + t = *ppte; *ppte++ = *cpte; *cpte = t; + *ip++ = + cpte++->pg_pfnum|ifrw->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 = ifrw->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. + * + * This routine is unlike if_wubaput in that pages are + * actually switched, rather than the UNIBUS maps temporarily + * remapped. + */ +deput(ifrw, m) + register struct ifrw *ifrw; + register struct mbuf *m; +{ + register struct mbuf *mp; + register caddr_t cp; + int cc; + register caddr_t dp; + register int i; + int x; + + cp = ifrw->ifrw_addr; + while (m) { + dp = mtod(m, char *); + if (claligned(cp) && claligned(dp) && m->m_len == CLBYTES) { + struct pte *cpte, *ppte; + int *ip; + + cpte = &Mbmap[mtocl(cp)*CLSIZE]; + ppte = &Mbmap[mtocl(dp)*CLSIZE]; + x = btop(cp - ifrw->ifrw_addr); + ip = (int *)&ifrw->ifrw_mr[x]; + for (i = 0; i < CLSIZE; i++) { + struct pte t; + t = *ppte; *ppte++ = *cpte; *cpte = t; + *ip++ = + cpte++->pg_pfnum|ifrw->ifrw_proto; + mtpr(TBIS, cp); + cp += NBPG; + mtpr(TBIS, dp); + dp += NBPG; + } + } else { + bcopy(mtod(m, caddr_t), cp, (unsigned)m->m_len); + cp += m->m_len; + } + MFREE(m, mp); + m = mp; + } + + cc = cp - ifrw->ifrw_addr; + return (cc); +} +#endif + +/* + * Process an ioctl request. + */ +deioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + register struct ifreq *ifr = (struct ifreq *)data; + int s = splimp(), error = 0; + + switch (cmd) { + + case SIOCSIFADDR: + if (ifp->if_flags & IFF_RUNNING) + if_rtinit(ifp, -1); /* delete previous route */ + desetaddr(ifp, (struct sockaddr_in *)&ifr->ifr_addr); + deinit(ifp->if_unit); + break; + + default: + error = EINVAL; + } + splx(s); + return (error); +} + +desetaddr(ifp, sin) + register struct ifnet *ifp; + register struct sockaddr_in *sin; +{ + + ifp->if_addr = *(struct sockaddr *)sin; + ifp->if_net = in_netof(sin->sin_addr); + ifp->if_host[0] = in_lnaof(sin->sin_addr); + 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; +} diff --git a/doc/if_dereg.h b/doc/if_dereg.h new file mode 100644 index 00000000..cd7d93af --- /dev/null +++ b/doc/if_dereg.h @@ -0,0 +1,183 @@ +/* + * DEC DEUNA interface + */ +struct dedevice { + union { + short p0_w; + char p0_b[2]; + } u_p0; +#define pcsr0 u_p0.p0_w +#define pclow u_p0.p0_b[0] +#define pchigh u_p0.p0_b[1] + short pcsr1; + short pcsr2; + short pcsr3; +}; + +/* + * PCSR 0 bit descriptions + */ +#define PCSR0_SERI 0x8000 /* Status error interrupt */ +#define PCSR0_PCEI 0x4000 /* Port command error interrupt */ +#define PCSR0_RXI 0x2000 /* Receive done interrupt */ +#define PCSR0_TXI 0x1000 /* Transmit done interrupt */ +#define PCSR0_DNI 0x0800 /* Done interrupt */ +#define PCSR0_RCBI 0x0400 /* Receive buffer unavail intrpt */ +#define PCSR0_FATI 0x0100 /* Fatal error interrupt */ +#define PCSR0_INTR 0x0080 /* Interrupt summary */ +#define PCSR0_INTE 0x0040 /* Interrupt enable */ +#define PCSR0_RSET 0x0020 /* DEUNA reset */ +#define PCSR0_CMASK 0x000f /* command mask */ + +#define PCSR0_BITS "\20\20SERI\17PCEI\16RXI\15TXI\14DNI\13RCBI\11FATI\10INTR\7INTE\6RSET" + +/* bits 0-3 are for the PORT_COMMAND */ +#define CMD_NOOP 0x0 +#define CMD_GETPCBB 0x1 /* Get PCB Block */ +#define CMD_GETCMD 0x2 /* Execute command in PCB */ +#define CMD_STEST 0x3 /* Self test mode */ +#define CMD_START 0x4 /* Reset xmit and receive ring ptrs */ +#define CMD_BOOT 0x5 /* Boot DEUNA */ +#define CMD_PDMD 0x8 /* Polling demand */ +#define CMD_TMRO 0x9 /* Sanity timer on */ +#define CMD_TMRF 0xa /* Sanity timer off */ +#define CMD_RSTT 0xb /* Reset sanity timer */ +#define CMD_STOP 0xf /* Suspend operation */ + +/* + * PCSR 1 bit descriptions + */ +#define PCSR1_XPWR 0x8000 /* Transceiver power BAD */ +#define PCSR1_ICAB 0x4000 /* Interconnect cabling BAD */ +#define PCSR1_STCODE 0x3f00 /* Self test error code */ +#define PCSR1_PCTO 0x0080 /* Port command timed out */ +#define PCSR1_ILLINT 0x0040 /* Illegal interrupt */ +#define PCSR1_TIMEOUT 0x0020 /* Timeout */ +#define PCSR1_POWER 0x0010 /* Power fail */ +#define PCSR1_RMTC 0x0008 /* Remote console reserved */ +#define PCSR1_STMASK 0x0007 /* State */ + +/* bit 0-3 are for STATE */ +#define STAT_RESET 0x0 +#define STAT_PRIMLD 0x1 /* Primary load */ +#define STAT_READY 0x2 +#define STAT_RUN 0x3 +#define STAT_UHALT 0x5 /* UNIBUS halted */ +#define STAT_NIHALT 0x6 /* NI halted */ +#define STAT_NIUHALT 0x7 /* NI and UNIBUS Halted */ + +#define PCSR1_BITS "\20\20XPWR\17ICAB\10PCTO\7ILLINT\6TIMEOUT\5POWER\4RMTC" + +/* + * Port Control Block Base + */ +struct de_pcbb { + short pcbb0; /* function */ + short pcbb2; /* command specific */ + short pcbb4; + short pcbb6; +}; + +/* PCBB function codes */ +#define FC_NOOP 0x00 /* NO-OP */ +#define FC_LSUADDR 0x01 /* Load and start microaddress */ +#define FC_RDDEFAULT 0x02 /* Read default physical address */ +#define FC_RDPHYAD 0x04 /* Read physical address */ +#define FC_WTPHYAD 0x05 /* Write physical address */ +#define FC_RDMULTI 0x06 /* Read multicast address list */ +#define FC_WTMULTI 0x07 /* Read multicast address list */ +#define FC_RDRING 0x08 /* Read ring format */ +#define FC_WTRING 0x09 /* Write ring format */ +#define FC_RDCNTS 0x0a /* Read counters */ +#define FC_RCCNTS 0x0b /* Read and clear counters */ +#define FC_RDMODE 0x0c /* Read mode */ +#define FC_WTMODE 0x0d /* Write mode */ +#define FC_RDSTATUS 0x0e /* Read port status */ +#define FC_RCSTATUS 0x0f /* Read and clear port status */ +#define FC_DUMPMEM 0x10 /* Dump internal memory */ +#define FC_LOADMEM 0x11 /* Load internal memory */ +#define FC_RDSYSID 0x12 /* Read system ID parameters */ +#define FC_WTSYSID 0x13 /* Write system ID parameters */ +#define FC_RDSERAD 0x14 /* Read load server address */ +#define FC_WTSERAD 0x15 /* Write load server address */ + +/* + * Unibus Data Block Base (UDBB) for ring buffers + */ +struct de_udbbuf { + short b_tdrbl; /* Transmit desc ring base low 16 bits */ + char b_tdrbh; /* Transmit desc ring base high 2 bits */ + char b_telen; /* Length of each transmit entry */ + short b_trlen; /* Number of entries in the XMIT desc ring */ + short b_rdrbl; /* Receive desc ring base low 16 bits */ + char b_rdrbh; /* Receive desc ring base high 2 bits */ + char b_relen; /* Length of each receive entry */ + short b_rrlen; /* Number of entries in the RECV desc ring */ +}; + +/* + * Transmit/Receive Ring Entry + */ +struct de_ring { + short r_slen; /* Segment length */ + short r_segbl; /* Segment address (low 16 bits) */ + char r_segbh; /* Segment address (hi 2 bits) */ + u_char r_flags; /* Status flags */ + u_short r_tdrerr; /* Errors */ +#define r_lenerr r_tdrerr + short r_rid; /* Request ID */ +}; + +#define XFLG_OWN 0x80 /* If 0 then owned by driver */ +#define XFLG_ERRS 0x40 /* Error summary */ +#define XFLG_MTCH 0x20 /* Address match on xmit request */ +#define XFLG_MORE 0x10 /* More than one entry required */ +#define XFLG_ONE 0x08 /* One collision encountered */ +#define XFLG_DEF 0x04 /* Transmit deferred */ +#define XFLG_STP 0x02 /* Start of packet */ +#define XFLG_ENP 0x01 /* End of packet */ + +#define XFLG_BITS "\10\10OWN\7ERRS\6MTCH\5MORE\4ONE\3DEF\2STP\1ENP" + +#define XERR_BUFL 0x8000 /* Buffer length error */ +#define XERR_UBTO 0x4000 /* UNIBUS tiemout +#define XERR_LCOL 0x1000 /* Late collision */ +#define XERR_LCAR 0x0800 /* Loss of carrier */ +#define XERR_RTRY 0x0400 /* Failed after 16 retries */ +#define XERR_TDR 0x03ff /* TDR value */ + +#define XERR_BITS "\20\20BUFL\17UBTO\15LCOL\14LCAR\13RTRY" + +#define RFLG_OWN 0x80 /* If 0 then owned by driver */ +#define RFLG_ERRS 0x40 /* Error summary */ +#define RFLG_FRAM 0x20 /* Framing error */ +#define RFLG_OFLO 0x10 /* Message overflow */ +#define RFLG_CRC 0x08 /* CRC error */ +#define RFLG_STP 0x02 /* Start of packet */ +#define RFLG_ENP 0x01 /* End of packet */ + +#define RFLG_BITS "\10\10OWN\7ERRS\6FRAM\5OFLO\4CRC\2STP\1ENP" + +#define RERR_BUFL 0x8000 /* Buffer length error */ +#define RERR_UBTO 0x4000 /* UNIBUS tiemout */ +#define RERR_NCHN 0x2000 /* No data chaining */ +#define RERR_MLEN 0x0fff /* Message length */ + +#define RERR_BITS "\20\20BUFL\17UBTO\16NCHN" + +/* mode description bits */ +#define MOD_HDX 0x0001 /* Half duplex mode */ +#define MOD_LOOP 0x0004 /* Enable internal loopback */ +#define MOD_DTCR 0x0008 /* Disables CRC generation */ +#define MOD_DMNT 0x0200 /* Disable maintenance features */ +#define MOD_ECT 0x0400 /* Enable collision test */ +#define MOD_TPAD 0x1000 /* Transmit message pad enable */ +#define MOD_DRDC 0x2000 /* Disable data chaining */ +#define MOD_ENAL 0x4000 /* Enable all multicast */ +#define MOD_PROM 0x8000 /* Enable promiscuous mode */ + +struct de_buf { + struct ether_header db_head; /* header */ + char db_data[ETHERMTU]; /* packet data */ + int db_crc; /* CRC - on receive only */ +};