134508Skarels /* 234508Skarels * Copyright (c) 1988 Regents of the University of California. 334508Skarels * All rights reserved. 434508Skarels * 534508Skarels * Redistribution and use in source and binary forms are permitted 6*34864Sbostic * provided that the above copyright notice and this paragraph are 7*34864Sbostic * duplicated in all such forms and that any documentation, 8*34864Sbostic * advertising materials, and other materials related to such 9*34864Sbostic * distribution and use acknowledge that the software was developed 10*34864Sbostic * by the University of California, Berkeley. The name of the 11*34864Sbostic * University may not be used to endorse or promote products derived 12*34864Sbostic * from this software without specific prior written permission. 13*34864Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34864Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34864Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1634508Skarels * 17*34864Sbostic * @(#)if_enp.c 7.2 (Berkeley) 06/29/88 1834508Skarels */ 1929649Ssam 2029649Ssam #include "enp.h" 2129649Ssam #if NENP > 0 2229649Ssam /* 2330986Ssam * CMC ENP-20 Ethernet Controller. 2429649Ssam */ 2529649Ssam #include "param.h" 2629649Ssam #include "systm.h" 2729649Ssam #include "mbuf.h" 2829649Ssam #include "buf.h" 2929649Ssam #include "protosw.h" 3029649Ssam #include "socket.h" 3129649Ssam #include "vmmac.h" 3230230Ssam #include "ioctl.h" 3329649Ssam #include "errno.h" 3430230Ssam #include "vmparam.h" 3530230Ssam #include "syslog.h" 3629649Ssam #include "uio.h" 3729649Ssam 3829649Ssam #include "../net/if.h" 3929649Ssam #include "../net/netisr.h" 4029649Ssam #include "../net/route.h" 4130230Ssam #ifdef INET 4229649Ssam #include "../netinet/in.h" 4329649Ssam #include "../netinet/in_systm.h" 4430230Ssam #include "../netinet/in_var.h" 4529649Ssam #include "../netinet/ip.h" 4629649Ssam #include "../netinet/ip_var.h" 4729649Ssam #include "../netinet/if_ether.h" 4830230Ssam #endif 4930230Ssam #ifdef NS 5030230Ssam #include "../netns/ns.h" 5130230Ssam #include "../netns/ns_if.h" 5230230Ssam #endif 5329649Ssam 5430230Ssam #include "../tahoe/cpu.h" 5530230Ssam #include "../tahoe/pte.h" 5630230Ssam #include "../tahoe/mtpr.h" 5730230Ssam 5829649Ssam #include "../tahoevba/vbavar.h" 5930230Ssam #include "../tahoeif/if_enpreg.h" 6029649Ssam 6130230Ssam #define ENPSTART 0xf02000 /* standard enp start addr */ 6230230Ssam #define ENPUNIT(dev) (minor(dev)) /* for enp ram devices */ 6330986Ssam /* macros for dealing with longs in i/o space */ 6430986Ssam #define ENPGETLONG(a) ((((u_short *)(a))[0] << 16)|(((u_short *)(a))[1])) 6530986Ssam #define ENPSETLONG(a,v) \ 6630986Ssam { register u_short *wp = (u_short *)(a); \ 6730986Ssam wp[0] = ((u_short *)&(v))[0]; wp[1] = ((u_short *)&(v))[1];} 6829649Ssam 6929649Ssam int enpprobe(), enpattach(), enpintr(); 7030298Ssam long enpstd[] = { 0xfff41000, 0xfff61000, 0 }; 7130230Ssam struct vba_device *enpinfo[NENP]; 7229649Ssam struct vba_driver enpdriver = 7330230Ssam { enpprobe, 0, enpattach, 0, enpstd, "enp", enpinfo, "enp-20", 0 }; 7429649Ssam 7530230Ssam int enpinit(), enpioctl(), enpreset(), enpoutput(); 7629649Ssam struct mbuf *enpget(); 7729649Ssam 7829649Ssam /* 7929649Ssam * Ethernet software status per interface. 8029649Ssam * 8129649Ssam * Each interface is referenced by a network interface structure, 8229649Ssam * es_if, which the routing code uses to locate the interface. 8329649Ssam * This structure contains the output queue for the interface, its address, ... 8429649Ssam */ 8530230Ssam struct enp_softc { 8630230Ssam struct arpcom es_ac; /* common ethernet structures */ 8730230Ssam #define es_if es_ac.ac_if 8830986Ssam #define es_addr es_ac.ac_enaddr 8930230Ssam short es_ivec; /* interrupt vector */ 9030230Ssam } enp_softc[NENP]; 9130230Ssam extern struct ifnet loif; 9229649Ssam 9330230Ssam enpprobe(reg, vi) 9430230Ssam caddr_t reg; 9530230Ssam struct vba_device *vi; 9629649Ssam { 9730230Ssam register br, cvec; /* must be r12, r11 */ 9830230Ssam register struct enpdevice *addr = (struct enpdevice *)reg; 9930230Ssam struct enp_softc *es = &enp_softc[vi->ui_unit]; 10029649Ssam 10130230Ssam #ifdef lint 10230295Ssam br = 0; cvec = br; br = cvec; 10330230Ssam enpintr(0); 10430230Ssam #endif 10530295Ssam if (badaddr((caddr_t)addr, 2) || badaddr((caddr_t)&addr->enp_ram[0], 2)) 10630230Ssam return (0); 10730230Ssam es->es_ivec = --vi->ui_hd->vh_lastiv; 10830230Ssam addr->enp_state = S_ENPRESET; /* reset by VERSAbus reset */ 10930230Ssam br = 0x14, cvec = es->es_ivec; /* XXX */ 11030230Ssam return (sizeof (struct enpdevice)); 11129649Ssam } 11229649Ssam 11329649Ssam /* 11429649Ssam * Interface exists: make available by filling in network interface 11529649Ssam * record. System will initialize the interface when it is ready 11629649Ssam * to accept packets. 11729649Ssam */ 11830230Ssam enpattach(ui) 11930230Ssam register struct vba_device *ui; 12029649Ssam { 12130230Ssam struct enp_softc *es = &enp_softc[ui->ui_unit]; 12230230Ssam register struct ifnet *ifp = &es->es_if; 12329649Ssam 12430230Ssam ifp->if_unit = ui->ui_unit; 12529649Ssam ifp->if_name = "enp"; 12629649Ssam ifp->if_mtu = ETHERMTU; 12729649Ssam ifp->if_init = enpinit; 12829649Ssam ifp->if_ioctl = enpioctl; 12929649Ssam ifp->if_output = enpoutput; 13029649Ssam ifp->if_reset = enpreset; 13130230Ssam ifp->if_flags = IFF_BROADCAST; 13229649Ssam if_attach(ifp); 13329649Ssam } 13429649Ssam 13529649Ssam /* 13630230Ssam * Reset of interface after "system" reset. 13729649Ssam */ 13830230Ssam enpreset(unit, vban) 13930230Ssam int unit, vban; 14029649Ssam { 14130230Ssam register struct vba_device *ui; 14229649Ssam 14330230Ssam if (unit >= NENP || (ui = enpinfo[unit]) == 0 || ui->ui_alive == 0 || 14430230Ssam ui->ui_vbanum != vban) 14530230Ssam return; 14630230Ssam printf(" enp%d", unit); 14729649Ssam enpinit(unit); 14829649Ssam } 14929649Ssam 15029649Ssam /* 15130230Ssam * Initialization of interface; clear recorded pending operations. 15229649Ssam */ 15330230Ssam enpinit(unit) 15430230Ssam int unit; 15529649Ssam { 15630230Ssam struct enp_softc *es = &enp_softc[unit]; 15730230Ssam register struct vba_device *ui = enpinfo[unit]; 15830230Ssam struct enpdevice *addr; 15930230Ssam register struct ifnet *ifp = &es->es_if; 16030230Ssam int s; 16129649Ssam 16230230Ssam if (ifp->if_addrlist == (struct ifaddr *)0) 16330230Ssam return; 16430230Ssam if ((ifp->if_flags & IFF_RUNNING) == 0) { 16530230Ssam addr = (struct enpdevice *)ui->ui_addr; 16630230Ssam s = splimp(); 16730230Ssam RESET_ENP(addr); 16830230Ssam DELAY(200000); 16930230Ssam es->es_if.if_flags |= IFF_RUNNING; 17030230Ssam splx(s); 17129649Ssam } 17229649Ssam } 17329649Ssam 17429649Ssam /* 17529649Ssam * Ethernet interface interrupt. 17629649Ssam */ 17730230Ssam enpintr(unit) 17830230Ssam int unit; 17929649Ssam { 18030230Ssam register struct enpdevice *addr; 18130230Ssam register BCB *bcbp; 18229649Ssam 18330230Ssam addr = (struct enpdevice *)enpinfo[unit]->ui_addr; 18430295Ssam #if ENP == 30 18530230Ssam if (!IS_ENP_INTR(addr)) 18629649Ssam return; 18730230Ssam ACK_ENP_INTR(addr); 18830295Ssam #endif 18930295Ssam while ((bcbp = (BCB *)ringget((RING *)&addr->enp_tohost )) != 0) { 19030295Ssam (void) enpread(&enp_softc[unit], bcbp); 19130295Ssam (void) ringput((RING *)&addr->enp_enpfree, bcbp); 19229649Ssam } 19329649Ssam } 19429649Ssam 19529649Ssam /* 19629649Ssam * Read input packet, examine its packet type, and enqueue it. 19729649Ssam */ 19830295Ssam enpread(es, bcbp) 19930230Ssam struct enp_softc *es; 20030230Ssam register BCB *bcbp; 20129649Ssam { 20229649Ssam register struct ether_header *enp; 20329649Ssam struct mbuf *m; 20430295Ssam int s, len, off, resid; 20529649Ssam register struct ifqueue *inq; 20629649Ssam 20729649Ssam es->es_if.if_ipackets++; 20829649Ssam /* 20929649Ssam * Get input data length. 21029649Ssam * Get pointer to ethernet header (in input buffer). 21129649Ssam * Deal with trailer protocol: if type is PUP trailer 21229649Ssam * get true type from first 16-bit word past data. 21329649Ssam * Remember that type was trailer by setting off. 21429649Ssam */ 21530230Ssam len = bcbp->b_msglen - sizeof (struct ether_header); 21630986Ssam enp = (struct ether_header *)ENPGETLONG(&bcbp->b_addr); 21730230Ssam #define enpdataaddr(enp, off, type) \ 21830230Ssam ((type)(((caddr_t)(((char *)enp)+sizeof (struct ether_header))+(off)))) 21930230Ssam enp->ether_type = ntohs((u_short)enp->ether_type); 22030230Ssam if (enp->ether_type >= ETHERTYPE_TRAIL && 22130230Ssam enp->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 22230230Ssam off = (enp->ether_type - ETHERTYPE_TRAIL) * 512; 22330230Ssam if (off >= ETHERMTU) 22430230Ssam goto setup; 22530230Ssam enp->ether_type = ntohs(*enpdataaddr(enp, off, u_short *)); 22630230Ssam resid = ntohs(*(enpdataaddr(enp, off+2, u_short *))); 22730230Ssam if (off + resid > len) 22830230Ssam goto setup; 22929649Ssam len = off + resid; 23030230Ssam } else 23129649Ssam off = 0; 23230230Ssam if (len == 0) 23330230Ssam goto setup; 23429649Ssam 23529649Ssam /* 23629649Ssam * Pull packet off interface. Off is nonzero if packet 23729649Ssam * has trailing header; enpget will then force this header 23829649Ssam * information to be at the front, but we still have to drop 23929649Ssam * the type and length which are at the front of any trailer data. 24029649Ssam */ 24130986Ssam m = enpget((u_char *)enp, len, off, &es->es_if); 24230230Ssam if (m == 0) 24330230Ssam goto setup; 24430230Ssam if (off) { 24530230Ssam struct ifnet *ifp; 24629649Ssam 24730230Ssam ifp = *(mtod(m, struct ifnet **)); 24829649Ssam m->m_off += 2 * sizeof (u_short); 24929649Ssam m->m_len -= 2 * sizeof (u_short); 25030230Ssam *(mtod(m, struct ifnet **)) = ifp; 25129649Ssam } 25230230Ssam switch (enp->ether_type) { 25329649Ssam 25429649Ssam #ifdef INET 25530230Ssam case ETHERTYPE_IP: 25629649Ssam schednetisr(NETISR_IP); 25729649Ssam inq = &ipintrq; 25829649Ssam break; 25930230Ssam #endif 26030230Ssam case ETHERTYPE_ARP: 26130230Ssam arpinput(&es->es_ac, m); 26230230Ssam goto setup; 26329649Ssam 26430230Ssam #ifdef NS 26530230Ssam case ETHERTYPE_NS: 26630230Ssam schednetisr(NETISR_NS); 26730230Ssam inq = &nsintrq; 26830230Ssam break; 26929649Ssam #endif 27030230Ssam default: 27129649Ssam m_freem(m); 27230230Ssam goto setup; 27329649Ssam } 27430230Ssam if (IF_QFULL(inq)) { 27529649Ssam IF_DROP(inq); 27629649Ssam m_freem(m); 27730230Ssam goto setup; 27829649Ssam } 27929649Ssam s = splimp(); 28029649Ssam IF_ENQUEUE(inq, m); 28129649Ssam splx(s); 28230230Ssam setup: 28330230Ssam return (0); 28429649Ssam } 28529649Ssam 28629649Ssam /* 28729649Ssam * Ethernet output routine. (called by user) 28829649Ssam * Encapsulate a packet of type family for the local net. 28929649Ssam * Use trailer local net encapsulation if enough data in first 29029649Ssam * packet leaves a multiple of 512 bytes of data in remainder. 29129649Ssam * If destination is this address or broadcast, send packet to 29229649Ssam * loop device to kludge around the fact that 3com interfaces can't 29329649Ssam * talk to themselves. 29429649Ssam */ 29529649Ssam enpoutput(ifp, m0, dst) 29630230Ssam struct ifnet *ifp; 29730230Ssam struct mbuf *m0; 29830230Ssam struct sockaddr *dst; 29929649Ssam { 30029649Ssam register struct enp_softc *es = &enp_softc[ifp->if_unit]; 30129649Ssam register struct mbuf *m = m0; 30229649Ssam register struct ether_header *enp; 30330295Ssam register int off; 30430230Ssam struct mbuf *mcopy = (struct mbuf *)0; 30530230Ssam int type, s, error, usetrailers; 30630230Ssam u_char edst[6]; 30730230Ssam struct in_addr idst; 30829649Ssam 30930230Ssam if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 31030230Ssam error = ENETDOWN; 31130230Ssam goto bad; 31230230Ssam } 31330230Ssam switch (dst->sa_family) { 31429649Ssam #ifdef INET 31529649Ssam case AF_INET: 31629649Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 31730230Ssam if (!arpresolve(&es->es_ac, m, &idst, edst, &usetrailers)) 31830230Ssam return (0); /* if not yet resolved */ 31930230Ssam if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr, 32030230Ssam sizeof (edst))) 32130230Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 32230230Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 32330230Ssam if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 32430230Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 32530230Ssam type = ETHERTYPE_TRAIL + (off>>9); 32630230Ssam m->m_off -= 2 * sizeof (u_short); 32730230Ssam m->m_len += 2 * sizeof (u_short); 32830986Ssam *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 32930986Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 33030230Ssam goto gottrailertype; 33129649Ssam } 33230230Ssam type = ETHERTYPE_IP; 33329649Ssam off = 0; 33429649Ssam goto gottype; 33529649Ssam #endif 33630230Ssam #ifdef NS 33730230Ssam case AF_NS: 33830230Ssam bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 33930230Ssam (caddr_t)edst, sizeof (edst)); 34030230Ssam if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost, sizeof (edst))) 34130230Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 34230230Ssam else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, 34330230Ssam sizeof (edst))) 34430230Ssam return (looutput(&loif, m, dst)); 34530230Ssam type = ETHERTYPE_NS; 34630230Ssam off = 0; 34730230Ssam goto gottype; 34829649Ssam #endif 34929649Ssam case AF_UNSPEC: 35029649Ssam enp = (struct ether_header *)dst->sa_data; 35130230Ssam bcopy((caddr_t)enp->ether_dhost, (caddr_t)edst, sizeof (edst)); 35229649Ssam type = enp->ether_type; 35329649Ssam goto gottype; 35429649Ssam 35529649Ssam default: 35630230Ssam log(LOG_ERR, "enp%d: can't handle af%d\n", 35730230Ssam ifp->if_unit, dst->sa_family); 35829649Ssam error = EAFNOSUPPORT; 35929649Ssam goto bad; 36029649Ssam } 36129649Ssam 36229649Ssam gottrailertype: 36329649Ssam /* 36429649Ssam * Packet to be sent as trailer: move first packet 36529649Ssam * (control information) to end of chain. 36629649Ssam */ 36729649Ssam while (m->m_next) 36829649Ssam m = m->m_next; 36929649Ssam m->m_next = m0; 37029649Ssam m = m0->m_next; 37129649Ssam m0->m_next = 0; 37229649Ssam m0 = m; 37329649Ssam 37429649Ssam gottype: 37529649Ssam /* 37629649Ssam * Add local net header. If no space in first mbuf, 37729649Ssam * allocate another. 37829649Ssam */ 37929649Ssam if (m->m_off > MMAXOFF || 38030230Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 38129649Ssam m = m_get(M_DONTWAIT, MT_HEADER); 38230230Ssam if (m == 0) { 38329649Ssam error = ENOBUFS; 38429649Ssam goto bad; 38529649Ssam } 38629649Ssam m->m_next = m0; 38729649Ssam m->m_off = MMINOFF; 38830230Ssam m->m_len = sizeof (struct ether_header); 38930230Ssam } else { 39030230Ssam m->m_off -= sizeof (struct ether_header); 39130230Ssam m->m_len += sizeof (struct ether_header); 39229649Ssam } 39329649Ssam enp = mtod(m, struct ether_header *); 39430230Ssam bcopy((caddr_t)edst, (caddr_t)enp->ether_dhost, sizeof (edst)); 39530986Ssam bcopy((caddr_t)es->es_addr, (caddr_t)enp->ether_shost, 39630986Ssam sizeof (es->es_addr)); 39730230Ssam enp->ether_type = htons((u_short)type); 39829649Ssam 39929649Ssam /* 40029649Ssam * Queue message on interface if possible 40129649Ssam */ 40229649Ssam s = splimp(); 40330230Ssam if (enpput(ifp->if_unit, m)) { 40429649Ssam error = ENOBUFS; 40529649Ssam goto qfull; 40629649Ssam } 40730230Ssam splx(s); 40829649Ssam es->es_if.if_opackets++; 40930230Ssam return (mcopy ? looutput(&loif, mcopy, dst) : 0); 41029649Ssam qfull: 41130230Ssam splx(s); 41229649Ssam m0 = m; 41329649Ssam bad: 41429649Ssam m_freem(m0); 41530230Ssam if (mcopy) 41630230Ssam m_freem(mcopy); 41730230Ssam return (error); 41829649Ssam } 41929649Ssam 42029649Ssam /* 42130230Ssam * Routine to copy from mbuf chain to transmitter buffer on the VERSAbus. 42229649Ssam */ 42330230Ssam enpput(unit, m) 42430230Ssam int unit; 42530230Ssam struct mbuf *m; 42629649Ssam { 42729649Ssam register BCB *bcbp; 42830230Ssam register struct enpdevice *addr; 42929649Ssam register struct mbuf *mp; 43029649Ssam register u_char *bp; 43130230Ssam register u_int len; 43230230Ssam u_char *mcp; 43329649Ssam 43430230Ssam addr = (struct enpdevice *)enpinfo[unit]->ui_addr; 43530295Ssam if (ringempty((RING *)&addr->enp_hostfree)) 43630230Ssam return (1); 43730295Ssam bcbp = (BCB *)ringget((RING *)&addr->enp_hostfree); 43829649Ssam bcbp->b_len = 0; 43930986Ssam bp = (u_char *)ENPGETLONG(&bcbp->b_addr); 44030230Ssam for (mp = m; mp; mp = mp->m_next) { 44129649Ssam len = mp->m_len; 44230230Ssam if (len == 0) 44329649Ssam continue; 44430230Ssam mcp = mtod(mp, u_char *); 44530230Ssam enpcopy(mcp, bp, len); 44629649Ssam bp += len; 44729649Ssam bcbp->b_len += len; 44829649Ssam } 44930986Ssam bcbp->b_len = MAX(ETHERMIN+sizeof (struct ether_header), bcbp->b_len); 45029649Ssam bcbp->b_reserved = 0; 45130295Ssam if (ringput((RING *)&addr->enp_toenp, bcbp) == 1) 45230230Ssam INTR_ENP(addr); 45329649Ssam m_freem(m); 45430230Ssam return (0); 45529649Ssam } 45629649Ssam 45729649Ssam /* 45830230Ssam * Routine to copy from VERSAbus memory into mbufs. 45929649Ssam * 46029649Ssam * Warning: This makes the fairly safe assumption that 46129649Ssam * mbufs have even lengths. 46229649Ssam */ 46329649Ssam struct mbuf * 46430230Ssam enpget(rxbuf, totlen, off0, ifp) 46530230Ssam u_char *rxbuf; 46630230Ssam int totlen, off0; 46730230Ssam struct ifnet *ifp; 46829649Ssam { 46930230Ssam register u_char *cp, *mcp; 47029649Ssam register struct mbuf *m; 47130230Ssam struct mbuf *top = 0, **mp = ⊤ 47230230Ssam int len, off = off0; 47329649Ssam 47430230Ssam cp = rxbuf + sizeof (struct ether_header); 47530230Ssam while (totlen > 0) { 47629649Ssam MGET(m, M_DONTWAIT, MT_DATA); 47729649Ssam if (m == 0) 47829649Ssam goto bad; 47930230Ssam if (off) { 48029649Ssam len = totlen - off; 48130230Ssam cp = rxbuf + sizeof (struct ether_header) + off; 48230230Ssam } else 48329649Ssam len = totlen; 48430230Ssam if (len >= NBPG) { 48530230Ssam MCLGET(m); 48630230Ssam if (m->m_len == CLBYTES) 48730230Ssam m->m_len = len = MIN(len, CLBYTES); 48830230Ssam else 48929649Ssam m->m_len = len = MIN(MLEN, len); 49030230Ssam } else { 49129649Ssam m->m_len = len = MIN(MLEN, len); 49229649Ssam m->m_off = MMINOFF; 49329649Ssam } 49429649Ssam mcp = mtod(m, u_char *); 49530230Ssam if (ifp) { 49630230Ssam /* 49730230Ssam * Prepend interface pointer to first mbuf. 49830230Ssam */ 49930230Ssam *(mtod(m, struct ifnet **)) = ifp; 50030230Ssam mcp += sizeof (ifp); 50130230Ssam len -= sizeof (ifp); 50230230Ssam ifp = (struct ifnet *)0; 50330230Ssam } 50430295Ssam enpcopy(cp, mcp, (u_int)len); 50529649Ssam cp += len; 50629649Ssam *mp = m; 50729649Ssam mp = &m->m_next; 50830230Ssam if (off == 0) { 50929649Ssam totlen -= len; 51029649Ssam continue; 51129649Ssam } 51229649Ssam off += len; 51330230Ssam if (off == totlen) { 51430230Ssam cp = rxbuf + sizeof (struct ether_header); 51529649Ssam off = 0; 51629649Ssam totlen = off0; 51729649Ssam } 51829649Ssam } 51929649Ssam return (top); 52029649Ssam bad: 52129649Ssam m_freem(top); 52229649Ssam return (0); 52329649Ssam } 52429649Ssam 52530230Ssam enpcopy(from, to, cnt) 52630295Ssam register u_char *from, *to; 52730295Ssam register u_int cnt; 52830230Ssam { 52930230Ssam register c; 53030230Ssam register short *f, *t; 53130230Ssam 53230230Ssam if (((int)from&01) && ((int)to&01)) { 53330230Ssam /* source & dest at odd addresses */ 53430230Ssam *to++ = *from++; 53530230Ssam --cnt; 53630230Ssam } 53730230Ssam if (cnt > 1 && (((int)to&01) == 0) && (((int)from&01) == 0)) { 53830230Ssam t = (short *)to; 53930230Ssam f = (short *)from; 54030230Ssam for (c = cnt>>1; c; --c) /* even address copy */ 54130230Ssam *t++ = *f++; 54230230Ssam cnt &= 1; 54330230Ssam if (cnt) { /* odd len */ 54430295Ssam from = (u_char *)f; 54530295Ssam to = (u_char *)t; 54630230Ssam *to = *from; 54730230Ssam } 54830230Ssam } 54930295Ssam while ((int)cnt-- > 0) /* one of the address(es) must be odd */ 55030230Ssam *to++ = *from++; 55130230Ssam } 55230230Ssam 55329649Ssam /* 55429649Ssam * Process an ioctl request. 55529649Ssam */ 55629649Ssam enpioctl(ifp, cmd, data) 55730230Ssam register struct ifnet *ifp; 55830230Ssam int cmd; 55930230Ssam caddr_t data; 56029649Ssam { 56130230Ssam register struct ifaddr *ifa = (struct ifaddr *)data; 56230230Ssam struct enpdevice *addr; 56330230Ssam int s = splimp(), error = 0; 56429649Ssam 56529649Ssam switch (cmd) { 56629649Ssam 56729649Ssam case SIOCSIFADDR: 56830230Ssam ifp->if_flags |= IFF_UP; 56930230Ssam switch (ifa->ifa_addr.sa_family) { 57030230Ssam #ifdef INET 57130230Ssam case AF_INET: 57230230Ssam enpinit(ifp->if_unit); 57330230Ssam ((struct arpcom *)ifp)->ac_ipaddr = 57430230Ssam IA_SIN(ifa)->sin_addr; 57530230Ssam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 57629649Ssam break; 57730230Ssam #endif 57830230Ssam #ifdef NS 57930230Ssam case AF_NS: { 58030230Ssam struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 58130230Ssam struct enp_softc *es = &enp_softc[ifp->if_unit]; 58230230Ssam 58330230Ssam if (!ns_nullhost(*ina)) { 58430230Ssam ifp->if_flags &= ~IFF_RUNNING; 58530230Ssam addr = (struct enpdevice *) 58630230Ssam enpinfo[ifp->if_unit]->ui_addr; 58730230Ssam enpsetaddr(ifp->if_unit, addr, 58830230Ssam ina->x_host.c_host); 58930230Ssam } else 59030986Ssam ina->x_host = *(union ns_host *)es->es_addr; 59130230Ssam enpinit(ifp->if_unit); 59230230Ssam break; 59329649Ssam } 59430230Ssam #endif 59530230Ssam default: 59630230Ssam enpinit(ifp->if_unit); 59730230Ssam break; 59829649Ssam } 59929649Ssam break; 60029649Ssam 60130230Ssam case SIOCSIFFLAGS: 60230230Ssam if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { 60330230Ssam enpinit(ifp->if_unit); /* reset board */ 60430230Ssam ifp->if_flags &= ~IFF_RUNNING; 60530230Ssam } else if (ifp->if_flags&IFF_UP && 60630230Ssam (ifp->if_flags&IFF_RUNNING) == 0) 60730230Ssam enpinit(ifp->if_unit); 60829649Ssam break; 60929649Ssam 61029649Ssam default: 61129649Ssam error = EINVAL; 61229649Ssam } 61330230Ssam splx(s); 61430230Ssam return (error); 61529649Ssam } 61629649Ssam 61730230Ssam enpsetaddr(unit, addr, enaddr) 61830230Ssam int unit; 61930230Ssam struct enpdevice *addr; 62030230Ssam u_char *enaddr; 62129649Ssam { 62229649Ssam 62330986Ssam enpcopy(enaddr, addr->enp_addr.e_baseaddr.ea_addr, 62430986Ssam sizeof (struct ether_addr)); 62530230Ssam enpinit(unit); 62630986Ssam enpgetaddr(unit, addr); 62729649Ssam } 62829649Ssam 62930986Ssam enpgetaddr(unit, addr) 63030986Ssam int unit; 63130986Ssam struct enpdevice *addr; 63230986Ssam { 63330986Ssam struct enp_softc *es = &enp_softc[unit]; 63430986Ssam 63530986Ssam enpcopy(addr->enp_addr.e_baseaddr.ea_addr, es->es_addr, 63630986Ssam sizeof (struct ether_addr)); 63730986Ssam printf("enp%d: hardware address %s\n", 63830986Ssam unit, ether_sprintf(es->es_addr)); 63930986Ssam } 64030986Ssam 64129649Ssam /* 64230230Ssam * Routines to synchronize enp and host. 64329649Ssam */ 64430295Ssam #ifdef notdef 64529649Ssam static 64630230Ssam ringinit(rp, size) 64730230Ssam register RING *rp; 64829649Ssam { 64929649Ssam 65029649Ssam rp->r_rdidx = rp->r_wrtidx = 0; 65129649Ssam rp->r_size = size; 65229649Ssam } 65329649Ssam 65429649Ssam static 65530295Ssam ringfull(rp) 65630230Ssam register RING *rp; 65729649Ssam { 65830295Ssam register short idx; 65930230Ssam 66030295Ssam idx = (rp->r_wrtidx + 1) & (rp->r_size-1); 66130295Ssam return (idx == rp->r_rdidx); 66229649Ssam } 66329649Ssam 66429649Ssam static 66530295Ssam fir(rp) 66630230Ssam register RING *rp; 66729649Ssam { 66829649Ssam 66930295Ssam return (rp->r_rdidx != rp->r_wrtidx ? rp->r_slot[rp->r_rdidx] : 0); 67029649Ssam } 67130295Ssam #endif 67229649Ssam 67329649Ssam static 67430295Ssam ringempty(rp) 67530295Ssam register RING *rp; 67630295Ssam { 67730295Ssam 67830295Ssam return (rp->r_rdidx == rp->r_wrtidx); 67930295Ssam } 68030295Ssam 68130295Ssam static 68230230Ssam ringput(rp, v) 68330230Ssam register RING *rp; 68430295Ssam BCB *v; 68529649Ssam { 68629649Ssam register int idx; 68729649Ssam 68829649Ssam idx = (rp->r_wrtidx + 1) & (rp->r_size-1); 68930230Ssam if (idx != rp->r_rdidx) { 69030986Ssam ENPSETLONG(&rp->r_slot[rp->r_wrtidx], v); 69129649Ssam rp->r_wrtidx = idx; 69230230Ssam if ((idx -= rp->r_rdidx) < 0) 69329649Ssam idx += rp->r_size; 69430230Ssam return (idx); /* num ring entries */ 69529649Ssam } 69630230Ssam return (0); 69729649Ssam } 69829649Ssam 69929649Ssam static 70030230Ssam ringget(rp) 70130230Ssam register RING *rp; 70229649Ssam { 70329649Ssam register int i = 0; 70429649Ssam 70530230Ssam if (rp->r_rdidx != rp->r_wrtidx) { 70630986Ssam i = ENPGETLONG(&rp->r_slot[rp->r_rdidx]); 70729649Ssam rp->r_rdidx = (++rp->r_rdidx) & (rp->r_size-1); 70829649Ssam } 70930230Ssam return (i); 71029649Ssam } 71129649Ssam 71230230Ssam /* 71330230Ssam * ENP Ram device. 71430230Ssam */ 71530230Ssam enpr_open(dev) 71630230Ssam dev_t dev; 71729649Ssam { 71830230Ssam register int unit = ENPUNIT(dev); 71930230Ssam struct vba_device *ui; 72030230Ssam struct enpdevice *addr; 72129649Ssam 72230230Ssam if (unit >= NENP || (ui = enpinfo[unit]) == 0 || ui->ui_alive == 0 || 72330230Ssam (addr = (struct enpdevice *)ui->ui_addr) == 0) 72430230Ssam return (ENODEV); 72530230Ssam if (addr->enp_state != S_ENPRESET) 72630230Ssam return (EACCES); /* enp is not in reset state, don't open */ 72730230Ssam return (0); 72829649Ssam } 72929649Ssam 73030295Ssam /*ARGSUSED*/ 73130230Ssam enpr_close(dev) 73230230Ssam dev_t dev; 73329649Ssam { 73429649Ssam 73530230Ssam return (0); 73629649Ssam } 73729649Ssam 73830230Ssam enpr_read(dev, uio) 73930230Ssam dev_t dev; 74030230Ssam register struct uio *uio; 74129649Ssam { 74230230Ssam register struct iovec *iov; 74330230Ssam struct enpdevice *addr; 74429649Ssam 74530230Ssam if (uio->uio_offset > RAM_SIZE) 74630230Ssam return (ENODEV); 74730295Ssam iov = uio->uio_iov; 74830230Ssam if (uio->uio_offset + iov->iov_len > RAM_SIZE) 74930230Ssam iov->iov_len = RAM_SIZE - uio->uio_offset; 75030230Ssam addr = (struct enpdevice *)enpinfo[ENPUNIT(dev)]->ui_addr; 75130986Ssam if (useracc(iov->iov_base, (unsigned)iov->iov_len, 0) == 0) 75230986Ssam return (EFAULT); 75330295Ssam enpcopy((u_char *)&addr->enp_ram[uio->uio_offset], 75430295Ssam (u_char *)iov->iov_base, (u_int)iov->iov_len); 75530230Ssam uio->uio_resid -= iov->iov_len; 75630230Ssam iov->iov_len = 0; 75730230Ssam return (0); 75829649Ssam } 75929649Ssam 76030230Ssam enpr_write(dev, uio) 76130230Ssam dev_t dev; 76230230Ssam register struct uio *uio; 76329649Ssam { 76430230Ssam register struct enpdevice *addr; 76530230Ssam register struct iovec *iov; 76629649Ssam 76730230Ssam addr = (struct enpdevice *)enpinfo[ENPUNIT(dev)]->ui_addr; 76830230Ssam iov = uio->uio_iov; 76930230Ssam if (uio->uio_offset > RAM_SIZE) 77030230Ssam return (ENODEV); 77130230Ssam if (uio->uio_offset + iov->iov_len > RAM_SIZE) 77230230Ssam iov->iov_len = RAM_SIZE - uio->uio_offset; 77330986Ssam if (useracc(iov->iov_base, (unsigned)iov->iov_len, 1) == 0) 77430986Ssam return (EFAULT); 77530295Ssam enpcopy((u_char *)iov->iov_base, 77630295Ssam (u_char *)&addr->enp_ram[uio->uio_offset], (u_int)iov->iov_len); 77730230Ssam uio->uio_resid -= iov->iov_len; 77830230Ssam iov->iov_len = 0; 77930230Ssam return (0); 78029649Ssam } 78129649Ssam 78230295Ssam /*ARGSUSED*/ 78330230Ssam enpr_ioctl(dev, cmd, data) 78430230Ssam dev_t dev; 78530230Ssam caddr_t data; 78629649Ssam { 78730230Ssam register unit = ENPUNIT(dev); 78830986Ssam struct enpdevice *addr; 78929649Ssam 79030230Ssam addr = (struct enpdevice *)enpinfo[unit]->ui_addr; 79130230Ssam switch(cmd) { 79230230Ssam 79330230Ssam case ENPIOGO: 79430986Ssam ENPSETLONG(&addr->enp_base, addr); 79530230Ssam addr->enp_intrvec = enp_softc[unit].es_ivec; 79630230Ssam ENP_GO(addr, ENPSTART); 79730230Ssam DELAY(200000); 79830230Ssam enpinit(unit); 79930986Ssam /* 80030986Ssam * Fetch Ethernet address after link level 80130986Ssam * is booted (firmware copies manufacturer's 80230986Ssam * address from on-board ROM). 80330986Ssam */ 80430986Ssam enpgetaddr(unit, addr); 80530986Ssam addr->enp_state = S_ENPRUN; 80630230Ssam break; 80730230Ssam 80830230Ssam case ENPIORESET: 80930230Ssam RESET_ENP(addr); 81030986Ssam addr->enp_state = S_ENPRESET; 81130230Ssam DELAY(100000); 81230230Ssam break; 81330986Ssam default: 81430986Ssam return (EINVAL); 81529649Ssam } 81630230Ssam return (0); 81729649Ssam } 81829649Ssam #endif 819