/* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Computer Consoles Inc. * * 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_enp.c 7.8 (Berkeley) 12/16/90 */ #include "enp.h" #if NENP > 0 /* * CMC ENP-20 Ethernet Controller. */ #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/ioctl.h" #include "sys/errno.h" #include "sys/vmparam.h" #include "sys/syslog.h" #include "sys/uio.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" #include "netinet/ip_var.h" #include "netinet/if_ether.h" #endif #ifdef NS #include "netns/ns.h" #include "netns/ns_if.h" #endif #include "../include/cpu.h" #include "../include/pte.h" #include "../include/mtpr.h" #include "../vba/vbavar.h" #include "../if/if_enpreg.h" #define ENPSTART 0xf02000 /* standard enp start addr */ #define ENPUNIT(dev) (minor(dev)) /* for enp ram devices */ /* macros for dealing with longs in i/o space */ #define ENPGETLONG(a) ((((u_short *)(a))[0] << 16)|(((u_short *)(a))[1])) #define ENPSETLONG(a,v) \ { register u_short *wp = (u_short *)(a); \ wp[0] = ((u_short *)&(v))[0]; wp[1] = ((u_short *)&(v))[1];} int enpprobe(), enpattach(), enpintr(); long enpstd[] = { 0xfff41000, 0xfff61000, 0 }; struct vba_device *enpinfo[NENP]; struct vba_driver enpdriver = { enpprobe, 0, enpattach, 0, enpstd, "enp", enpinfo, "enp-20", 0 }; int enpinit(), enpioctl(), enpreset(), enpoutput(), enpstart(); struct mbuf *enpget(); /* * 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, ... */ struct enp_softc { struct arpcom es_ac; /* common ethernet structures */ #define es_if es_ac.ac_if #define es_addr es_ac.ac_enaddr short es_ivec; /* interrupt vector */ } enp_softc[NENP]; extern struct ifnet loif; enpprobe(reg, vi) caddr_t reg; struct vba_device *vi; { register br, cvec; /* must be r12, r11 */ register struct enpdevice *addr = (struct enpdevice *)reg; struct enp_softc *es = &enp_softc[vi->ui_unit]; #ifdef lint br = 0; cvec = br; br = cvec; enpintr(0); #endif if (badaddr((caddr_t)addr, 2) || badaddr((caddr_t)&addr->enp_ram[0], 2)) return (0); es->es_ivec = --vi->ui_hd->vh_lastiv; addr->enp_state = S_ENPRESET; /* reset by VERSAbus reset */ br = 0x14, cvec = es->es_ivec; /* XXX */ return (sizeof (struct enpdevice)); } /* * Interface exists: make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. */ enpattach(ui) register struct vba_device *ui; { struct enp_softc *es = &enp_softc[ui->ui_unit]; register struct ifnet *ifp = &es->es_if; ifp->if_unit = ui->ui_unit; ifp->if_name = "enp"; ifp->if_mtu = ETHERMTU; ifp->if_init = enpinit; ifp->if_ioctl = enpioctl; ifp->if_output = ether_output; ifp->if_start = enpstart; ifp->if_reset = enpreset; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; if_attach(ifp); } /* * Reset of interface after "system" reset. */ enpreset(unit, vban) int unit, vban; { register struct vba_device *ui; if (unit >= NENP || (ui = enpinfo[unit]) == 0 || ui->ui_alive == 0 || ui->ui_vbanum != vban) return; printf(" enp%d", unit); enpinit(unit); } /* * Initialization of interface; clear recorded pending operations. */ enpinit(unit) int unit; { struct enp_softc *es = &enp_softc[unit]; register struct vba_device *ui = enpinfo[unit]; struct enpdevice *addr; register struct ifnet *ifp = &es->es_if; int s; if (ifp->if_addrlist == (struct ifaddr *)0) return; if ((ifp->if_flags & IFF_RUNNING) == 0) { addr = (struct enpdevice *)ui->ui_addr; s = splimp(); RESET_ENP(addr); DELAY(200000); es->es_if.if_flags |= IFF_RUNNING; splx(s); } } /* * Ethernet interface interrupt. */ enpintr(unit) int unit; { register struct enpdevice *addr; register BCB *bcbp; addr = (struct enpdevice *)enpinfo[unit]->ui_addr; #if ENP == 30 if (!IS_ENP_INTR(addr)) return; ACK_ENP_INTR(addr); #endif while ((bcbp = (BCB *)ringget((RING *)&addr->enp_tohost )) != 0) { enpread(&enp_softc[unit], bcbp); (void) ringput((RING *)&addr->enp_enpfree, bcbp); } } /* * Read input packet, examine its packet type, and enqueue it. */ enpread(es, bcbp) struct enp_softc *es; register BCB *bcbp; { register struct ether_header *enp; struct mbuf *m; int s, len, off, resid; es->es_if.if_ipackets++; /* * 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 = bcbp->b_msglen - sizeof (struct ether_header); enp = (struct ether_header *)ENPGETLONG(&bcbp->b_addr); #define enpdataaddr(enp, off, type) \ ((type)(((caddr_t)(((char *)enp)+sizeof (struct ether_header))+(off)))) enp->ether_type = ntohs((u_short)enp->ether_type); if (enp->ether_type >= ETHERTYPE_TRAIL && enp->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { off = (enp->ether_type - ETHERTYPE_TRAIL) * 512; if (off >= ETHERMTU) return; enp->ether_type = ntohs(*enpdataaddr(enp, off, u_short *)); resid = ntohs(*(enpdataaddr(enp, off+2, u_short *))); if (off + resid > len) return; len = off + resid; } else off = 0; if (len == 0) return; /* * Pull packet off interface. Off is nonzero if packet * has trailing header; enpget will then force this header * information to be at the front. */ m = enpget((u_char *)enp, len, off, &es->es_if); if (m == 0) return; ether_input(&es->es_if, enp, m); } enpstart(ifp) struct ifnet *ifp; { if (enpput(ifp)) return (ENOBUFS); else return (0); } /* * Routine to copy from mbuf chain to transmitter buffer on the VERSAbus. */ enpput(ifp) struct ifnet *ifp; { register BCB *bcbp; register struct enpdevice *addr; register struct mbuf *mp; register u_char *bp; register u_int len; int unit = ifp->if_unit, ret = 1; struct mbuf *m; addr = (struct enpdevice *)enpinfo[unit]->ui_addr; again: if (ringempty((RING *)&addr->enp_hostfree)) { /* ifp->if_flags |= IFF_OACTIVE; */ return (ret); } IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) { ifp->if_flags &= ~IFF_OACTIVE; return (0); } bcbp = (BCB *)ringget((RING *)&addr->enp_hostfree); bcbp->b_len = 0; bp = (u_char *)ENPGETLONG(&bcbp->b_addr); for (mp = m; mp; mp = mp->m_next) { len = mp->m_len; if (len == 0) continue; enpcopy(mtod(mp, u_char *), bp, len); bp += len; bcbp->b_len += len; } bcbp->b_len = max(ETHERMIN+sizeof (struct ether_header), bcbp->b_len); bcbp->b_reserved = 0; if (ringput((RING *)&addr->enp_toenp, bcbp) == 1) INTR_ENP(addr); m_freem(m); ret = 0; goto again; } /* * Routine to copy from VERSAbus memory into mbufs. * * Warning: This makes the fairly safe assumption that * mbufs have even lengths. */ struct mbuf * enpget(rxbuf, totlen, off, ifp) u_char *rxbuf; int totlen, off; struct ifnet *ifp; { register u_char *cp; register struct mbuf *m; struct mbuf *top = 0, **mp = ⊤ int len; u_char *packet_end; rxbuf += sizeof (struct ether_header); cp = rxbuf; packet_end = cp + totlen; if (off) { off += 2 * sizeof(u_short); totlen -= 2 *sizeof(u_short); cp = rxbuf + off; } MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) return (0); m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = totlen; m->m_len = MHLEN; while (totlen > 0) { if (top) { MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { m_freem(top); return (0); } m->m_len = MLEN; } len = min(totlen, (packet_end - cp)); if (len >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if (m->m_flags & M_EXT) m->m_len = len = min(len, MCLBYTES); else len = m->m_len; } else { /* * Place initial small packet/header at end of mbuf. */ if (len < m->m_len) { if (top == 0 && len + max_linkhdr <= m->m_len) m->m_data += max_linkhdr; m->m_len = len; } else len = m->m_len; } enpcopy(cp, mtod(m, u_char *), (u_int)len); *mp = m; mp = &m->m_next; totlen -= len; cp += len; if (cp == packet_end) cp = rxbuf; } return (top); } enpcopy(from, to, cnt) register u_char *from, *to; register u_int cnt; { register c; register short *f, *t; if (((int)from&01) && ((int)to&01)) { /* source & dest at odd addresses */ *to++ = *from++; --cnt; } if (cnt > 1 && (((int)to&01) == 0) && (((int)from&01) == 0)) { t = (short *)to; f = (short *)from; for (c = cnt>>1; c; --c) /* even address copy */ *t++ = *f++; cnt &= 1; if (cnt) { /* odd len */ from = (u_char *)f; to = (u_char *)t; *to = *from; } } while ((int)cnt-- > 0) /* one of the address(es) must be odd */ *to++ = *from++; } /* * Process an ioctl request. */ enpioctl(ifp, cmd, data) register struct ifnet *ifp; int cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *)data; struct enpdevice *addr; int s = splimp(), error = 0; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: enpinit(ifp->if_unit); ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); break; #endif #ifdef NS case AF_NS: { struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; struct enp_softc *es = &enp_softc[ifp->if_unit]; if (!ns_nullhost(*ina)) { ifp->if_flags &= ~IFF_RUNNING; addr = (struct enpdevice *) enpinfo[ifp->if_unit]->ui_addr; enpsetaddr(ifp->if_unit, addr, ina->x_host.c_host); } else ina->x_host = *(union ns_host *)es->es_addr; enpinit(ifp->if_unit); break; } #endif default: enpinit(ifp->if_unit); break; } break; case SIOCSIFFLAGS: if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { enpinit(ifp->if_unit); /* reset board */ ifp->if_flags &= ~IFF_RUNNING; } else if (ifp->if_flags&IFF_UP && (ifp->if_flags&IFF_RUNNING) == 0) enpinit(ifp->if_unit); break; default: error = EINVAL; } splx(s); return (error); } enpsetaddr(unit, addr, enaddr) int unit; struct enpdevice *addr; u_char *enaddr; { enpcopy(enaddr, addr->enp_addr.e_baseaddr.ea_addr, sizeof (struct ether_addr)); enpinit(unit); enpgetaddr(unit, addr); } enpgetaddr(unit, addr) int unit; struct enpdevice *addr; { struct enp_softc *es = &enp_softc[unit]; enpcopy(addr->enp_addr.e_baseaddr.ea_addr, es->es_addr, sizeof (struct ether_addr)); printf("enp%d: hardware address %s\n", unit, ether_sprintf(es->es_addr)); } /* * Routines to synchronize enp and host. */ #ifdef notdef static ringinit(rp, size) register RING *rp; { rp->r_rdidx = rp->r_wrtidx = 0; rp->r_size = size; } static ringfull(rp) register RING *rp; { register short idx; idx = (rp->r_wrtidx + 1) & (rp->r_size-1); return (idx == rp->r_rdidx); } static fir(rp) register RING *rp; { return (rp->r_rdidx != rp->r_wrtidx ? rp->r_slot[rp->r_rdidx] : 0); } #endif static ringempty(rp) register RING *rp; { return (rp->r_rdidx == rp->r_wrtidx); } static ringput(rp, v) register RING *rp; BCB *v; { register int idx; idx = (rp->r_wrtidx + 1) & (rp->r_size-1); if (idx != rp->r_rdidx) { ENPSETLONG(&rp->r_slot[rp->r_wrtidx], v); rp->r_wrtidx = idx; if ((idx -= rp->r_rdidx) < 0) idx += rp->r_size; return (idx); /* num ring entries */ } return (0); } static ringget(rp) register RING *rp; { register int i = 0; if (rp->r_rdidx != rp->r_wrtidx) { i = ENPGETLONG(&rp->r_slot[rp->r_rdidx]); rp->r_rdidx = (++rp->r_rdidx) & (rp->r_size-1); } return (i); } /* * ENP Ram device. */ enpr_open(dev) dev_t dev; { register int unit = ENPUNIT(dev); struct vba_device *ui; struct enpdevice *addr; if (unit >= NENP || (ui = enpinfo[unit]) == 0 || ui->ui_alive == 0 || (addr = (struct enpdevice *)ui->ui_addr) == 0) return (ENODEV); if (addr->enp_state != S_ENPRESET) return (EACCES); /* enp is not in reset state, don't open */ return (0); } /*ARGSUSED*/ enpr_close(dev) dev_t dev; { return (0); } enpr_read(dev, uio) dev_t dev; register struct uio *uio; { register struct iovec *iov; struct enpdevice *addr; if (uio->uio_offset > RAM_SIZE) return (ENODEV); iov = uio->uio_iov; if (uio->uio_offset + iov->iov_len > RAM_SIZE) iov->iov_len = RAM_SIZE - uio->uio_offset; addr = (struct enpdevice *)enpinfo[ENPUNIT(dev)]->ui_addr; if (useracc(iov->iov_base, (unsigned)iov->iov_len, 0) == 0) return (EFAULT); enpcopy((u_char *)&addr->enp_ram[uio->uio_offset], (u_char *)iov->iov_base, (u_int)iov->iov_len); uio->uio_resid -= iov->iov_len; iov->iov_len = 0; return (0); } enpr_write(dev, uio) dev_t dev; register struct uio *uio; { register struct enpdevice *addr; register struct iovec *iov; addr = (struct enpdevice *)enpinfo[ENPUNIT(dev)]->ui_addr; iov = uio->uio_iov; if (uio->uio_offset > RAM_SIZE) return (ENODEV); if (uio->uio_offset + iov->iov_len > RAM_SIZE) iov->iov_len = RAM_SIZE - uio->uio_offset; if (useracc(iov->iov_base, (unsigned)iov->iov_len, 1) == 0) return (EFAULT); enpcopy((u_char *)iov->iov_base, (u_char *)&addr->enp_ram[uio->uio_offset], (u_int)iov->iov_len); uio->uio_resid -= iov->iov_len; uio->uio_offset += iov->iov_len; iov->iov_len = 0; return (0); } /*ARGSUSED*/ enpr_ioctl(dev, cmd, data) dev_t dev; caddr_t data; { register unit = ENPUNIT(dev); struct enpdevice *addr; addr = (struct enpdevice *)enpinfo[unit]->ui_addr; switch(cmd) { case ENPIOGO: ENPSETLONG(&addr->enp_base, addr); addr->enp_intrvec = enp_softc[unit].es_ivec; ENP_GO(addr, ENPSTART); DELAY(200000); enpinit(unit); /* * Fetch Ethernet address after link level * is booted (firmware copies manufacturer's * address from on-board ROM). */ enpgetaddr(unit, addr); addr->enp_state = S_ENPRUN; break; case ENPIORESET: RESET_ENP(addr); addr->enp_state = S_ENPRESET; DELAY(100000); break; default: return (EINVAL); } return (0); } #endif