xref: /csrg-svn/sys/tahoe/if/if_enp.c (revision 34864)
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 = &top;
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