home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1982, 1986 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)if_en.c 7.7 (Berkeley) 12/16/90
- */
-
- #include "en.h"
- #if NEN > 0
-
- /*
- * Xerox prototype (3 Mb) Ethernet interface driver.
- */
- #include "../include/pte.h"
-
- #include "sys/param.h"
- #include "sys/systm.h"
- #include "sys/mbuf.h"
- #include "sys/buf.h"
- #include "sys/protosw.h"
- #include "sys/socket.h"
- #include "sys/vmmac.h"
- #include "sys/errno.h"
- #include "sys/ioctl.h"
-
- #include "net/if.h"
- #include "net/netisr.h"
- #include "net/route.h"
-
- #ifdef INET
- #include "netinet/in.h"
- #include "netinet/in_systm.h"
- #include "netinet/in_var.h"
- #include "netinet/ip.h"
- #endif
-
- #ifdef PUP
- #include "netpup/pup.h"
- #include "netpup/ether.h"
- #endif
-
- #ifdef NS
- #include "netns/ns.h"
- #include "netns/ns_if.h"
- #endif
-
- #include "../include/cpu.h"
- #include "../include/mtpr.h"
- #include "if_en.h"
- #include "if_enreg.h"
- #include "if_uba.h"
- #include "../uba/ubareg.h"
- #include "../uba/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(),oldenoutput(),enreset(),enioctl(), enstart();
-
- #ifdef notdef
- /*
- * If you need to byte swap IP's in the system, define
- * this and do a SIOCSIFFLAGS at boot time.
- */
- #define ENF_SWABIPS 0x1000
- #endif
-
- /*
- * 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_host; /* hardware host number */
- 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 */
- short es_nsactive; /* is interface enabled for ns? */
- } 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;
- 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];
-
- es->es_if.if_unit = ui->ui_unit;
- es->es_if.if_name = "en";
- es->es_if.if_mtu = ENMTU;
- es->es_if.if_flags = IFF_BROADCAST;
- es->es_if.if_init = eninit;
- es->es_if.if_output = oldenoutput;
- es->es_if.if_start = enstart;
- es->es_if.if_ioctl = enioctl;
- 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 (es->es_if.if_addrlist == (struct ifaddr *)0)
- return;
- 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_RUNNING;
- enxint(unit);
- splx(s);
- }
-
- 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;
- register struct en_header *en;
- 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;
- }
- en = mtod(m, struct en_header *);
- dest = en->en_dhost;
- en->en_shost = es->es_host;
- es->es_olen = if_wubaput(&es->es_ifuba, m);
- #ifdef ENF_SWABIPS
- /*
- * The Xerox interface does word at a time DMA, so
- * someone must do byte swapping of user data if high
- * and low ender machines are to communicate. It doesn't
- * belong here, but certain people depend on it, so...
- *
- * Should swab everybody, but this is a kludge anyway.
- */
- if (es->es_if.if_flags & ENF_SWABIPS) {
- en = (struct en_header *)es->es_ifuba.ifu_w.ifrw_addr;
- if (en->en_type == ENTYPE_IP)
- enswab((caddr_t)(en + 1), (caddr_t)(en + 1),
- es->es_olen - sizeof (struct en_header) + 1);
- }
- #endif
-
- /*
- * 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);
- }
-
- #ifdef notdef
- struct sockproto enproto = { AF_ETHERLINK };
- struct sockaddr_en endst = { sizeof(endst), AF_ETHERLINK };
- struct sockaddr_en ensrc = { sizeof(ensrc), AF_ETHERLINK };
- #endif
- /*
- * 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, s;
-
- 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);
- en->en_type = ntohs(en->en_type);
- #define endataaddr(en, off, type) ((type)(((caddr_t)((en)+1)+(off))))
- if (en->en_type >= ENTYPE_TRAIL &&
- en->en_type < ENTYPE_TRAIL+ENTYPE_NTRAILER) {
- off = (en->en_type - ENTYPE_TRAIL) * 512;
- if (off > ENMTU)
- goto setup; /* sanity */
- en->en_type = ntohs(*endataaddr(en, off, u_short *));
- resid = ntohs(*(endataaddr(en, off+2, u_short *)));
- if (off + resid > len)
- goto setup; /* sanity */
- len = off + resid;
- } else
- off = 0;
- if (len == 0)
- goto setup;
- #ifdef ENF_SWABIPS
- if (es->es_if.if_flags & ENF_SWABIPS && en->en_type == ENTYPE_IP)
- enswab((caddr_t)(en + 1), (caddr_t)(en + 1), len);
- #endif
- /*
- * 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, &es->es_if);
- if (m == 0)
- goto setup;
- switch (en->en_type) {
-
- #ifdef INET
- case ENTYPE_IP:
- schednetisr(NETISR_IP);
- inq = &ipintrq;
- break;
- #endif
- #ifdef PUP
- case ENTYPE_PUP:
- rpup_input(m);
- goto setup;
- #endif
- #ifdef NS
- case ETHERTYPE_NS:
- if (es->es_nsactive) {
- schednetisr(NETISR_NS);
- inq = &nsintrq;
- } else {
- m_freem(m);
- goto setup;
- }
- break;
- #endif
-
- default:
- #ifdef notdef
- enproto.sp_protocol = en->en_type;
- endst.sen_host = en->en_dhost;
- endst.sen_net = ensrc.sen_net = es->es_if.if_net;
- ensrc.sen_host = en->en_shost;
- raw_input(m, &enproto,
- (struct sockaddr *)&ensrc, (struct sockaddr *)&endst);
- #else
- m_freem(m);
- #endif
- goto setup;
- }
-
- s = splimp();
- if (IF_QFULL(inq)) {
- IF_DROP(inq);
- m_freem(m);
- } else
- IF_ENQUEUE(inq, m);
- splx(s);
-
- 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.
- */
- oldenoutput(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;
-
- if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
- error = ENETDOWN;
- goto bad;
- }
- switch (dst->sa_family) {
-
- #ifdef INET
- case AF_INET:
- {
- struct in_addr in;
-
- in = ((struct sockaddr_in *)dst)->sin_addr;
- if (in_broadcast(in))
- dest = EN_BROADCAST;
- else
- dest = in_lnaof(in);
- }
- if (dest >= 0x100) {
- error = EPERM; /* ??? */
- goto bad;
- }
- off = m->m_pkthdr.len - m->m_len;
- /* need per host negotiation */
- if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
- if (off > 0 && (off & 0x1ff) == 0 &&
- (m->m_flags & M_EXT) == 0 &&
- m->m_data >= m->m_pktdat + 2 * sizeof (u_short)) {
- type = ENTYPE_TRAIL + (off>>9);
- m->m_data -= 2 * sizeof (u_short);
- m->m_len += 2 * sizeof (u_short);
- *mtod(m, u_short *) = htons((u_short)ENTYPE_IP);
- *(mtod(m, u_short *) + 1) = ntohs((u_short)m->m_len);
- goto gottrailertype;
- }
- type = ENTYPE_IP;
- off = 0;
- goto gottype;
- #endif
- #ifdef NS
- case AF_NS:
- {
- u_char *up;
-
- type = ETHERTYPE_NS;
- up = ((struct sockaddr_ns *)dst)->sns_addr.x_host.c_host;
- if (*up & 1)
- dest = EN_BROADCAST;
- else
- dest = up[5];
-
- off = 0;
- goto gottype;
- }
- #endif
- #ifdef PUP
- case AF_PUP:
- dest = ((struct sockaddr_pup *)dst)->spup_host;
- type = ENTYPE_PUP;
- off = 0;
- goto gottype;
- #endif
-
- #ifdef notdef
- case AF_ETHERLINK:
- goto gotheader;
- #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.
- */
- M_PREPEND(m, sizeof (struct en_header), M_DONTWAIT);
- if (m == NULL)
- return (ENOBUFS);
- en = mtod(m, struct en_header *);
- /* add en_shost later */
- en->en_dhost = dest;
- en->en_type = htons((u_short)type);
-
- #ifdef notdef
- gotheader:
- #endif
- /*
- * 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);
- }
-
- /*
- * Process an ioctl request.
- */
- enioctl(ifp, cmd, data)
- register struct ifnet *ifp;
- int cmd;
- caddr_t data;
- {
- register struct en_softc *es = ((struct en_softc *)ifp);
- struct ifaddr *ifa = (struct ifaddr *) data;
- int s = splimp(), error = 0;
- struct endevice *enaddr;
-
- switch (cmd) {
-
- case SIOCSIFADDR:
- enaddr = (struct endevice *)eninfo[ifp->if_unit]->ui_addr;
- es->es_host = (~enaddr->en_addr) & 0xff;
- /*
- * Attempt to check agreement of protocol address
- * and board address.
- */
- switch (ifa->ifa_addr->sa_family) {
- case AF_INET:
- if (in_lnaof(IA_SIN(ifa)->sin_addr) != es->es_host)
- return (EADDRNOTAVAIL);
- break;
- #ifdef NS
- case AF_NS:
- if (IA_SNS(ifa)->sns_addr.x_host.c_host[5]
- != es->es_host)
- return (EADDRNOTAVAIL);
- es->es_nsactive = 1;
- break;
- #endif
- }
- ifp->if_flags |= IFF_UP;
- if ((ifp->if_flags & IFF_RUNNING) == 0)
- eninit(ifp->if_unit);
- break;
-
- default:
- error = EINVAL;
- break;
- }
- splx(s);
- return (error);
- }
-
- #ifdef ENF_SWABIPS
- /*
- * Swab bytes
- * Jeffrey Mogul, Stanford
- */
- enswab(from, to, n)
- register unsigned char *from, *to;
- register int n;
- {
- register unsigned long temp;
-
- if ((n <= 0) || (n > 0xFFFF)) {
- printf("enswab: bad len %d\n", n);
- return;
- }
-
- n >>= 1; n++;
- #define STEP {temp = *from++;*to++ = *from++;*to++ = temp;}
- /* round to multiple of 8 */
- while ((--n) & 07)
- STEP;
- n >>= 3;
- while (--n >= 0) {
- STEP; STEP; STEP; STEP;
- STEP; STEP; STEP; STEP;
- }
- }
- #endif
- #endif
-