xref: /csrg-svn/sys/tahoe/if/if_enp.c (revision 30295)
1*30295Ssam /*	if_enp.c	1.3	86/12/15	*/
229649Ssam 
329649Ssam #include "enp.h"
429649Ssam #if NENP > 0
529649Ssam /*
630230Ssam  * Modified 3Com Ethernet Controller interface
729649Ssam  * enp modifications added S. F. Holmgren
830230Ssam  *
930230Ssam  * UNTESTED WITH 4.3
1029649Ssam  */
1129649Ssam #include "param.h"
1229649Ssam #include "systm.h"
1329649Ssam #include "mbuf.h"
1429649Ssam #include "buf.h"
1529649Ssam #include "protosw.h"
1629649Ssam #include "socket.h"
1729649Ssam #include "vmmac.h"
1830230Ssam #include "ioctl.h"
1929649Ssam #include "errno.h"
2030230Ssam #include "vmparam.h"
2130230Ssam #include "syslog.h"
2229649Ssam #include "uio.h"
2329649Ssam 
2429649Ssam #include "../net/if.h"
2529649Ssam #include "../net/netisr.h"
2629649Ssam #include "../net/route.h"
2730230Ssam #ifdef INET
2829649Ssam #include "../netinet/in.h"
2929649Ssam #include "../netinet/in_systm.h"
3030230Ssam #include "../netinet/in_var.h"
3129649Ssam #include "../netinet/ip.h"
3229649Ssam #include "../netinet/ip_var.h"
3329649Ssam #include "../netinet/if_ether.h"
3430230Ssam #endif
3530230Ssam #ifdef NS
3630230Ssam #include "../netns/ns.h"
3730230Ssam #include "../netns/ns_if.h"
3830230Ssam #endif
3929649Ssam 
4030230Ssam #include "../tahoe/cpu.h"
4130230Ssam #include "../tahoe/pte.h"
4230230Ssam #include "../tahoe/mtpr.h"
4330230Ssam 
4429649Ssam #include "../tahoevba/vbavar.h"
4530230Ssam #include "../tahoeif/if_enpreg.h"
4629649Ssam 
4730230Ssam #define	ENPVEC	0xc1
4830230Ssam #define ENPSTART	0xf02000	/* standard enp start addr */
4930230Ssam #define	ENPUNIT(dev)	(minor(dev))	/* for enp ram devices */
5029649Ssam 
5129649Ssam int	enpprobe(), enpattach(), enpintr();
5230230Ssam long	enpstd[] = { 0xf41000, 0xf61000, 0 };
5330230Ssam struct  vba_device *enpinfo[NENP];
5429649Ssam struct  vba_driver enpdriver =
5530230Ssam     { enpprobe, 0, enpattach, 0, enpstd, "enp", enpinfo, "enp-20", 0 };
5629649Ssam 
5730230Ssam int	enpinit(), enpioctl(), enpreset(), enpoutput();
5829649Ssam struct  mbuf *enpget();
5929649Ssam 
6029649Ssam /*
6129649Ssam  * Ethernet software status per interface.
6229649Ssam  *
6329649Ssam  * Each interface is referenced by a network interface structure,
6429649Ssam  * es_if, which the routing code uses to locate the interface.
6529649Ssam  * This structure contains the output queue for the interface, its address, ...
6629649Ssam  */
6730230Ssam struct  enp_softc {
6830230Ssam 	struct  arpcom es_ac;           /* common ethernet structures */
6930230Ssam #define es_if		es_ac.ac_if
7030230Ssam #define es_enaddr	es_ac.ac_enaddr
7130230Ssam 	short	es_flags;		/* flags for devices */
7230230Ssam 	short	es_ivec;		/* interrupt vector */
7330230Ssam 	struct	pte *es_map;		/* map for dual ported memory */
7430230Ssam 	caddr_t	es_ram;			/* virtual address of mapped memory */
7530230Ssam } enp_softc[NENP];
7630230Ssam extern	struct ifnet loif;
7729649Ssam 
7830230Ssam enpprobe(reg, vi)
7930230Ssam 	caddr_t reg;
8030230Ssam 	struct vba_device *vi;
8129649Ssam {
8230230Ssam 	register br, cvec;		/* must be r12, r11 */
8330230Ssam 	register struct enpdevice *addr = (struct enpdevice *)reg;
8430230Ssam 	struct enp_softc *es = &enp_softc[vi->ui_unit];
8529649Ssam 
8630230Ssam #ifdef lint
87*30295Ssam 	br = 0; cvec = br; br = cvec;
8830230Ssam 	enpintr(0);
8930230Ssam #endif
90*30295Ssam 	if (badaddr((caddr_t)addr, 2) || badaddr((caddr_t)&addr->enp_ram[0], 2))
9130230Ssam 		return (0);
9230230Ssam 	es->es_ivec = --vi->ui_hd->vh_lastiv;
9330230Ssam 	addr->enp_state = S_ENPRESET;		/* reset by VERSAbus reset */
9430230Ssam 	br = 0x14, cvec = es->es_ivec;		/* XXX */
9530230Ssam 	return (sizeof (struct enpdevice));
9629649Ssam }
9729649Ssam 
9829649Ssam /*
9929649Ssam  * Interface exists: make available by filling in network interface
10029649Ssam  * record.  System will initialize the interface when it is ready
10129649Ssam  * to accept packets.
10229649Ssam  */
10330230Ssam enpattach(ui)
10430230Ssam 	register struct vba_device *ui;
10529649Ssam {
10630230Ssam 	struct enp_softc *es = &enp_softc[ui->ui_unit];
10730230Ssam 	register struct ifnet *ifp = &es->es_if;
10830230Ssam 	register struct enpdevice *addr = (struct enpdevice *)ui->ui_addr;
10929649Ssam 
11030230Ssam 	ifp->if_unit = ui->ui_unit;
11129649Ssam 	ifp->if_name = "enp";
11229649Ssam 	ifp->if_mtu = ETHERMTU;
11330230Ssam 	/*
11430230Ssam 	 * Get station's addresses.
11530230Ssam 	 */
116*30295Ssam 	enpcopy((u_char *)&addr->enp_addr.e_baseaddr, es->es_enaddr,
11730230Ssam 	    sizeof (es->es_enaddr));
11830230Ssam 	printf("enp%d: hardware address %s\n", ui->ui_unit,
11930230Ssam 	    ether_sprintf(es->es_enaddr));
12030230Ssam 	/*
12130230Ssam 	 * Allocate and map ram.
12230230Ssam 	 */
12330230Ssam 	vbmemalloc(128, ((caddr_t)addr)+0x1000, &es->es_map, &es->es_ram);
12429649Ssam 
12529649Ssam 	ifp->if_init = enpinit;
12629649Ssam 	ifp->if_ioctl = enpioctl;
12729649Ssam 	ifp->if_output = enpoutput;
12829649Ssam 	ifp->if_reset = enpreset;
12930230Ssam 	ifp->if_flags = IFF_BROADCAST;
13029649Ssam 	if_attach(ifp);
13129649Ssam }
13229649Ssam 
13329649Ssam /*
13430230Ssam  * Reset of interface after "system" reset.
13529649Ssam  */
13630230Ssam enpreset(unit, vban)
13730230Ssam 	int unit, vban;
13829649Ssam {
13930230Ssam 	register struct vba_device *ui;
14029649Ssam 
14130230Ssam 	if (unit >= NENP || (ui = enpinfo[unit]) == 0 || ui->ui_alive == 0 ||
14230230Ssam 	    ui->ui_vbanum != vban)
14330230Ssam 		return;
14430230Ssam 	printf(" enp%d", unit);
14529649Ssam 	enpinit(unit);
14629649Ssam }
14729649Ssam 
14829649Ssam /*
14930230Ssam  * Initialization of interface; clear recorded pending operations.
15029649Ssam  */
15130230Ssam enpinit(unit)
15230230Ssam 	int unit;
15329649Ssam {
15430230Ssam 	struct enp_softc *es = &enp_softc[unit];
15530230Ssam 	register struct vba_device *ui = enpinfo[unit];
15630230Ssam 	struct enpdevice *addr;
15730230Ssam 	register struct ifnet *ifp = &es->es_if;
15830230Ssam 	int s;
15929649Ssam 
16030230Ssam 	if (ifp->if_addrlist == (struct ifaddr *)0)
16130230Ssam 		return;
16230230Ssam 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
16330230Ssam 		addr = (struct enpdevice *)ui->ui_addr;
16430230Ssam 		s = splimp();
16530230Ssam 		RESET_ENP(addr);
16630230Ssam 		DELAY(200000);
16730230Ssam 		addr->enp_intrvec = es->es_ivec;
16830230Ssam 		es->es_if.if_flags |= IFF_RUNNING;
16930230Ssam 		splx(s);
17029649Ssam 	}
17129649Ssam }
17229649Ssam 
17329649Ssam /*
17429649Ssam  * Ethernet interface interrupt.
17529649Ssam  */
17630230Ssam enpintr(unit)
17730230Ssam 	int unit;
17829649Ssam {
17930230Ssam 	register struct enpdevice *addr;
18030230Ssam 	register BCB *bcbp;
18129649Ssam 
18230230Ssam 	addr = (struct enpdevice *)enpinfo[unit]->ui_addr;
183*30295Ssam #if ENP == 30
18430230Ssam 	if (!IS_ENP_INTR(addr))
18529649Ssam 		return;
18630230Ssam 	ACK_ENP_INTR(addr);
187*30295Ssam #endif
188*30295Ssam 	while ((bcbp = (BCB *)ringget((RING *)&addr->enp_tohost )) != 0) {
189*30295Ssam 		(void) enpread(&enp_softc[unit], bcbp);
190*30295Ssam 		(void) ringput((RING *)&addr->enp_enpfree, bcbp);
19129649Ssam 	}
19229649Ssam }
19329649Ssam 
19429649Ssam /*
19529649Ssam  * Read input packet, examine its packet type, and enqueue it.
19629649Ssam  */
197*30295Ssam enpread(es, bcbp)
19830230Ssam 	struct enp_softc *es;
19930230Ssam 	register BCB *bcbp;
20029649Ssam {
20129649Ssam 	register struct ether_header *enp;
20229649Ssam 	struct mbuf *m;
203*30295Ssam 	int s, len, off, resid;
20429649Ssam 	register struct ifqueue *inq;
20529649Ssam 
20629649Ssam 	es->es_if.if_ipackets++;
20729649Ssam 	/*
20829649Ssam 	 * Get input data length.
20929649Ssam 	 * Get pointer to ethernet header (in input buffer).
21029649Ssam 	 * Deal with trailer protocol: if type is PUP trailer
21129649Ssam 	 * get true type from first 16-bit word past data.
21229649Ssam 	 * Remember that type was trailer by setting off.
21329649Ssam 	 */
21430230Ssam 	len = bcbp->b_msglen - sizeof (struct ether_header);
21529649Ssam 	enp = (struct ether_header *)bcbp->b_addr;
21630230Ssam #define enpdataaddr(enp, off, type) \
21730230Ssam     ((type)(((caddr_t)(((char *)enp)+sizeof (struct ether_header))+(off))))
21830230Ssam 	enp->ether_type = ntohs((u_short)enp->ether_type);
21930230Ssam 	if (enp->ether_type >= ETHERTYPE_TRAIL &&
22030230Ssam 	    enp->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
22130230Ssam 		off = (enp->ether_type - ETHERTYPE_TRAIL) * 512;
22230230Ssam 		if (off >= ETHERMTU)
22330230Ssam 			goto setup;
22430230Ssam 		enp->ether_type = ntohs(*enpdataaddr(enp, off, u_short *));
22530230Ssam 		resid = ntohs(*(enpdataaddr(enp, off+2, u_short *)));
22630230Ssam 		if (off + resid > len)
22730230Ssam 			goto setup;
22829649Ssam 		len = off + resid;
22930230Ssam 	} else
23029649Ssam 		off = 0;
23130230Ssam 	if (len == 0)
23230230Ssam 		goto setup;
23329649Ssam 
23429649Ssam 	/*
23529649Ssam 	 * Pull packet off interface.  Off is nonzero if packet
23629649Ssam 	 * has trailing header; enpget will then force this header
23729649Ssam 	 * information to be at the front, but we still have to drop
23829649Ssam 	 * the type and length which are at the front of any trailer data.
23929649Ssam 	 */
24030230Ssam 	m = enpget(bcbp->b_addr, len, off, &es->es_if);
24130230Ssam 	if (m == 0)
24230230Ssam 		goto setup;
24330230Ssam 	if (off) {
24430230Ssam 		struct ifnet *ifp;
24529649Ssam 
24630230Ssam 		ifp = *(mtod(m, struct ifnet **));
24729649Ssam 		m->m_off += 2 * sizeof (u_short);
24829649Ssam 		m->m_len -= 2 * sizeof (u_short);
24930230Ssam 		*(mtod(m, struct ifnet **)) = ifp;
25029649Ssam 	}
25130230Ssam 	switch (enp->ether_type) {
25229649Ssam 
25329649Ssam #ifdef INET
25430230Ssam 	case ETHERTYPE_IP:
25529649Ssam 		schednetisr(NETISR_IP);
25629649Ssam 		inq = &ipintrq;
25729649Ssam 		break;
25830230Ssam #endif
25930230Ssam 	case ETHERTYPE_ARP:
26030230Ssam 		arpinput(&es->es_ac, m);
26130230Ssam 		goto setup;
26229649Ssam 
26330230Ssam #ifdef NS
26430230Ssam 	case ETHERTYPE_NS:
26530230Ssam 		schednetisr(NETISR_NS);
26630230Ssam 		inq = &nsintrq;
26730230Ssam 		break;
26829649Ssam #endif
26930230Ssam 	default:
27029649Ssam 		m_freem(m);
27130230Ssam 		goto setup;
27229649Ssam 	}
27330230Ssam 	if (IF_QFULL(inq)) {
27429649Ssam 		IF_DROP(inq);
27529649Ssam 		m_freem(m);
27630230Ssam 		goto setup;
27729649Ssam 	}
27829649Ssam 	s = splimp();
27929649Ssam 	IF_ENQUEUE(inq, m);
28029649Ssam 	splx(s);
28130230Ssam setup:
28230230Ssam 	return (0);
28329649Ssam }
28429649Ssam 
28529649Ssam /*
28629649Ssam  * Ethernet output routine. (called by user)
28729649Ssam  * Encapsulate a packet of type family for the local net.
28829649Ssam  * Use trailer local net encapsulation if enough data in first
28929649Ssam  * packet leaves a multiple of 512 bytes of data in remainder.
29029649Ssam  * If destination is this address or broadcast, send packet to
29129649Ssam  * loop device to kludge around the fact that 3com interfaces can't
29229649Ssam  * talk to themselves.
29329649Ssam  */
29429649Ssam enpoutput(ifp, m0, dst)
29530230Ssam 	struct ifnet *ifp;
29630230Ssam 	struct mbuf *m0;
29730230Ssam 	struct sockaddr *dst;
29829649Ssam {
29929649Ssam 	register struct enp_softc *es = &enp_softc[ifp->if_unit];
30029649Ssam 	register struct mbuf *m = m0;
30129649Ssam 	register struct ether_header *enp;
302*30295Ssam 	register int off;
30330230Ssam 	struct mbuf *mcopy = (struct mbuf *)0;
30430230Ssam 	int type, s, error, usetrailers;
30530230Ssam 	u_char edst[6];
30630230Ssam 	struct in_addr idst;
30729649Ssam 
30830230Ssam 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
30930230Ssam 		error = ENETDOWN;
31030230Ssam 		goto bad;
31130230Ssam 	}
31230230Ssam 	switch (dst->sa_family) {
31329649Ssam #ifdef INET
31429649Ssam 	case AF_INET:
31529649Ssam 		idst = ((struct sockaddr_in *)dst)->sin_addr;
31630230Ssam 		if (!arpresolve(&es->es_ac, m, &idst, edst, &usetrailers))
31730230Ssam 			return (0);	/* if not yet resolved */
31830230Ssam 		if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr,
31930230Ssam 		    sizeof (edst)))
32030230Ssam 			mcopy = m_copy(m, 0, (int)M_COPYALL);
32130230Ssam 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
32230230Ssam 		if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
32330230Ssam 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
32430230Ssam 			type = ETHERTYPE_TRAIL + (off>>9);
32530230Ssam 			m->m_off -= 2 * sizeof (u_short);
32630230Ssam 			m->m_len += 2 * sizeof (u_short);
32730230Ssam 			*mtod(m, u_short *) = ETHERTYPE_IP;
32830230Ssam 			*(mtod(m, u_short *) + 1) = m->m_len;
32930230Ssam 			goto gottrailertype;
33029649Ssam 		}
33130230Ssam 		type = ETHERTYPE_IP;
33229649Ssam 		off = 0;
33329649Ssam 		goto gottype;
33429649Ssam #endif
33530230Ssam #ifdef NS
33630230Ssam 	case AF_NS:
33730230Ssam 		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
33830230Ssam 		    (caddr_t)edst, sizeof (edst));
33930230Ssam 		if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost, sizeof (edst)))
34030230Ssam 			mcopy = m_copy(m, 0, (int)M_COPYALL);
34130230Ssam 		else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost,
34230230Ssam 		    sizeof (edst)))
34330230Ssam 			return (looutput(&loif, m, dst));
34430230Ssam 		type = ETHERTYPE_NS;
34530230Ssam 		off = 0;
34630230Ssam 		goto gottype;
34729649Ssam #endif
34829649Ssam 	case AF_UNSPEC:
34929649Ssam 		enp = (struct ether_header *)dst->sa_data;
35030230Ssam 		bcopy((caddr_t)enp->ether_dhost, (caddr_t)edst, sizeof (edst));
35129649Ssam 		type = enp->ether_type;
35229649Ssam 		goto gottype;
35329649Ssam 
35429649Ssam 	default:
35530230Ssam 		log(LOG_ERR, "enp%d: can't handle af%d\n",
35630230Ssam 		    ifp->if_unit, dst->sa_family);
35729649Ssam 		error = EAFNOSUPPORT;
35829649Ssam 		goto bad;
35929649Ssam 	}
36029649Ssam 
36129649Ssam gottrailertype:
36229649Ssam 	/*
36329649Ssam 	 * Packet to be sent as trailer: move first packet
36429649Ssam 	 * (control information) to end of chain.
36529649Ssam 	 */
36629649Ssam 	while (m->m_next)
36729649Ssam 		m = m->m_next;
36829649Ssam 	m->m_next = m0;
36929649Ssam 	m = m0->m_next;
37029649Ssam 	m0->m_next = 0;
37129649Ssam 	m0 = m;
37229649Ssam 
37329649Ssam gottype:
37429649Ssam 	/*
37529649Ssam          * Add local net header.  If no space in first mbuf,
37629649Ssam          * allocate another.
37729649Ssam          */
37829649Ssam 	if (m->m_off > MMAXOFF ||
37930230Ssam 	    MMINOFF + sizeof (struct ether_header) > m->m_off) {
38029649Ssam 		m = m_get(M_DONTWAIT, MT_HEADER);
38130230Ssam 		if (m == 0) {
38229649Ssam 			error = ENOBUFS;
38329649Ssam 			goto bad;
38429649Ssam 		}
38529649Ssam 		m->m_next = m0;
38629649Ssam 		m->m_off = MMINOFF;
38730230Ssam 		m->m_len = sizeof (struct ether_header);
38830230Ssam 	} else {
38930230Ssam 		m->m_off -= sizeof (struct ether_header);
39030230Ssam 		m->m_len += sizeof (struct ether_header);
39129649Ssam 	}
39229649Ssam 	enp = mtod(m, struct ether_header *);
39330230Ssam 	bcopy((caddr_t)edst, (caddr_t)enp->ether_dhost, sizeof (edst));
39430230Ssam 	bcopy((caddr_t)es->es_enaddr, (caddr_t)enp->ether_shost,
39530230Ssam 	    sizeof (es->es_enaddr));
39630230Ssam 	enp->ether_type = htons((u_short)type);
39729649Ssam 
39829649Ssam 	/*
39929649Ssam 	 * Queue message on interface if possible
40029649Ssam 	 */
40129649Ssam 	s = splimp();
40230230Ssam 	if (enpput(ifp->if_unit, m)) {
40329649Ssam 		error = ENOBUFS;
40429649Ssam 		goto qfull;
40529649Ssam 	}
40630230Ssam 	splx(s);
40729649Ssam 	es->es_if.if_opackets++;
40830230Ssam 	return (mcopy ? looutput(&loif, mcopy, dst) : 0);
40929649Ssam qfull:
41030230Ssam 	splx(s);
41129649Ssam 	m0 = m;
41229649Ssam bad:
41329649Ssam 	m_freem(m0);
41430230Ssam 	if (mcopy)
41530230Ssam 		m_freem(mcopy);
41630230Ssam 	return (error);
41729649Ssam }
41829649Ssam 
41929649Ssam /*
42030230Ssam  * Routine to copy from mbuf chain to transmitter buffer on the VERSAbus.
42129649Ssam  */
42230230Ssam enpput(unit, m)
42330230Ssam 	int unit;
42430230Ssam 	struct mbuf *m;
42529649Ssam {
42629649Ssam 	register BCB *bcbp;
42730230Ssam 	register struct enpdevice *addr;
42829649Ssam 	register struct mbuf *mp;
42929649Ssam 	register u_char *bp;
43030230Ssam 	register u_int len;
43130230Ssam 	u_char *mcp;
43229649Ssam 
43330230Ssam 	addr = (struct enpdevice *)enpinfo[unit]->ui_addr;
434*30295Ssam 	if (ringempty((RING *)&addr->enp_hostfree))
43530230Ssam 		return (1);
436*30295Ssam 	bcbp = (BCB *)ringget((RING *)&addr->enp_hostfree);
43729649Ssam 	bcbp->b_len = 0;
43829649Ssam 	bp = (u_char *)bcbp->b_addr;
43930230Ssam 	for (mp = m; mp; mp = mp->m_next) {
44029649Ssam 		len = mp->m_len;
44130230Ssam 		if (len == 0)
44229649Ssam 			continue;
44330230Ssam 		mcp = mtod(mp, u_char *);
44430230Ssam 		enpcopy(mcp, bp, len);
44529649Ssam 		bp += len;
44629649Ssam 		bcbp->b_len += len;
44729649Ssam 	}
448*30295Ssam 	bcbp->b_len = MAX(ETHERMIN, bcbp->b_len);
44929649Ssam 	bcbp->b_reserved = 0;
450*30295Ssam 	if (ringput((RING *)&addr->enp_toenp, bcbp) == 1)
45130230Ssam 		INTR_ENP(addr);
45229649Ssam 	m_freem(m);
45330230Ssam 	return (0);
45429649Ssam }
45529649Ssam 
45629649Ssam /*
45730230Ssam  * Routine to copy from VERSAbus memory into mbufs.
45829649Ssam  *
45929649Ssam  * Warning: This makes the fairly safe assumption that
46029649Ssam  * mbufs have even lengths.
46129649Ssam  */
46229649Ssam struct mbuf *
46330230Ssam enpget(rxbuf, totlen, off0, ifp)
46430230Ssam 	u_char *rxbuf;
46530230Ssam 	int totlen, off0;
46630230Ssam 	struct ifnet *ifp;
46729649Ssam {
46830230Ssam 	register u_char *cp, *mcp;
46929649Ssam 	register struct mbuf *m;
47030230Ssam 	struct mbuf *top = 0, **mp = &top;
47130230Ssam 	int len, off = off0;
47229649Ssam 
47330230Ssam 	cp = rxbuf + sizeof (struct ether_header);
47430230Ssam 	while (totlen > 0) {
47529649Ssam 		MGET(m, M_DONTWAIT, MT_DATA);
47629649Ssam 		if (m == 0)
47729649Ssam 			goto bad;
47830230Ssam 		if (off) {
47929649Ssam 			len = totlen - off;
48030230Ssam 			cp = rxbuf + sizeof (struct ether_header) + off;
48130230Ssam 		} else
48229649Ssam 			len = totlen;
48330230Ssam 		if (len >= NBPG) {
48430230Ssam 			MCLGET(m);
48530230Ssam 			if (m->m_len == CLBYTES)
48630230Ssam 				m->m_len = len = MIN(len, CLBYTES);
48730230Ssam 			else
48829649Ssam 				m->m_len = len = MIN(MLEN, len);
48930230Ssam 		} else {
49029649Ssam 			m->m_len = len = MIN(MLEN, len);
49129649Ssam 			m->m_off = MMINOFF;
49229649Ssam 		}
49329649Ssam 		mcp = mtod(m, u_char *);
49430230Ssam 		if (ifp) {
49530230Ssam 			/*
49630230Ssam 			 * Prepend interface pointer to first mbuf.
49730230Ssam 			 */
49830230Ssam 			*(mtod(m, struct ifnet **)) = ifp;
49930230Ssam 			mcp += sizeof (ifp);
50030230Ssam 			len -= sizeof (ifp);
50130230Ssam 			ifp = (struct ifnet *)0;
50230230Ssam 		}
503*30295Ssam 		enpcopy(cp, mcp, (u_int)len);
50429649Ssam 		cp += len;
50529649Ssam 		*mp = m;
50629649Ssam 		mp = &m->m_next;
50730230Ssam 		if (off == 0) {
50829649Ssam 			totlen -= len;
50929649Ssam 			continue;
51029649Ssam 		}
51129649Ssam 		off += len;
51230230Ssam 		if (off == totlen) {
51330230Ssam 			cp = rxbuf + sizeof (struct ether_header);
51429649Ssam 			off = 0;
51529649Ssam 			totlen = off0;
51629649Ssam 		}
51729649Ssam 	}
51829649Ssam 	return (top);
51929649Ssam bad:
52029649Ssam 	m_freem(top);
52129649Ssam 	return (0);
52229649Ssam }
52329649Ssam 
52430230Ssam enpcopy(from, to, cnt)
525*30295Ssam 	register u_char *from, *to;
526*30295Ssam 	register u_int cnt;
52730230Ssam {
52830230Ssam 	register c;
52930230Ssam 	register short *f, *t;
53030230Ssam 
53130230Ssam 	if (((int)from&01) && ((int)to&01)) {
53230230Ssam 		/* source & dest at odd addresses */
53330230Ssam 		*to++ = *from++;
53430230Ssam 		--cnt;
53530230Ssam 	}
53630230Ssam 	if (cnt > 1 && (((int)to&01) == 0) && (((int)from&01) == 0)) {
53730230Ssam 		t = (short *)to;
53830230Ssam 		f = (short *)from;
53930230Ssam 		for (c = cnt>>1; c; --c)	/* even address copy */
54030230Ssam 			*t++ = *f++;
54130230Ssam 		cnt &= 1;
54230230Ssam 		if (cnt) {			/* odd len */
543*30295Ssam 			from = (u_char *)f;
544*30295Ssam 			to = (u_char *)t;
54530230Ssam 			*to = *from;
54630230Ssam 		}
54730230Ssam 	}
548*30295Ssam 	while ((int)cnt-- > 0)	/* one of the address(es) must be odd */
54930230Ssam 		*to++ = *from++;
55030230Ssam }
55130230Ssam 
55229649Ssam /*
55329649Ssam  * Process an ioctl request.
55429649Ssam  */
55529649Ssam enpioctl(ifp, cmd, data)
55630230Ssam 	register struct ifnet *ifp;
55730230Ssam 	int cmd;
55830230Ssam 	caddr_t data;
55929649Ssam {
56030230Ssam 	register struct ifaddr *ifa = (struct ifaddr *)data;
56130230Ssam 	struct enpdevice *addr;
56230230Ssam 	int s = splimp(), error = 0;
56329649Ssam 
56429649Ssam 	switch (cmd) {
56529649Ssam 
56629649Ssam 	case SIOCSIFADDR:
56730230Ssam 		ifp->if_flags |= IFF_UP;
56830230Ssam 		switch (ifa->ifa_addr.sa_family) {
56930230Ssam #ifdef INET
57030230Ssam 		case AF_INET:
57130230Ssam 			enpinit(ifp->if_unit);
57230230Ssam 			((struct arpcom *)ifp)->ac_ipaddr =
57330230Ssam 			    IA_SIN(ifa)->sin_addr;
57430230Ssam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
57529649Ssam 			break;
57630230Ssam #endif
57730230Ssam #ifdef NS
57830230Ssam 		case AF_NS: {
57930230Ssam 			struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
58030230Ssam 			struct enp_softc *es = &enp_softc[ifp->if_unit];
58130230Ssam 
58230230Ssam 			if (!ns_nullhost(*ina)) {
58330230Ssam 				ifp->if_flags &= ~IFF_RUNNING;
58430230Ssam 				addr = (struct enpdevice *)
58530230Ssam 				    enpinfo[ifp->if_unit]->ui_addr;
58630230Ssam 				enpsetaddr(ifp->if_unit, addr,
58730230Ssam 				    ina->x_host.c_host);
58830230Ssam 			} else
58930230Ssam 				ina->x_host = *(union ns_host *)es->es_enaddr;
59030230Ssam 			enpinit(ifp->if_unit);
59130230Ssam 			break;
59229649Ssam 		}
59330230Ssam #endif
59430230Ssam 		default:
59530230Ssam 			enpinit(ifp->if_unit);
59630230Ssam 			break;
59729649Ssam 		}
59829649Ssam 		break;
59929649Ssam 
60030230Ssam 	case SIOCSIFFLAGS:
60130230Ssam 		if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) {
60230230Ssam 			enpinit(ifp->if_unit);		/* reset board */
60330230Ssam 			ifp->if_flags &= ~IFF_RUNNING;
60430230Ssam 		} else if (ifp->if_flags&IFF_UP &&
60530230Ssam 		     (ifp->if_flags&IFF_RUNNING) == 0)
60630230Ssam 			enpinit(ifp->if_unit);
60729649Ssam 		break;
60829649Ssam 
60929649Ssam 	default:
61029649Ssam 		error = EINVAL;
61129649Ssam 	}
61230230Ssam 	splx(s);
61330230Ssam 	return (error);
61429649Ssam }
61529649Ssam 
61630230Ssam enpsetaddr(unit, addr, enaddr)
61730230Ssam 	int unit;
61830230Ssam 	struct enpdevice *addr;
61930230Ssam 	u_char *enaddr;
62029649Ssam {
62130230Ssam 	u_char *cp;
62230230Ssam 	int i, code;
62329649Ssam 
62430230Ssam 	cp = &addr->enp_addr.e_baseaddr.ea_addr[0];
62530230Ssam 	for (i = 0; i < 6; i++)
62630230Ssam 		*cp++ = ~*enaddr++;
627*30295Ssam 	enpcopy((u_char *)&addr->enp_addr.e_listsize, (u_char *)&code,
628*30295Ssam 	    sizeof (code));
62930230Ssam 	code |= E_ADDR_SUPP;
630*30295Ssam 	enpcopy((u_char *)&code, (u_char *)&addr->enp_addr.e_listsize,
631*30295Ssam 	    sizeof (code));
63230230Ssam 	enpinit(unit);
63329649Ssam }
63429649Ssam 
63529649Ssam /*
63630230Ssam  * Routines to synchronize enp and host.
63729649Ssam  */
638*30295Ssam #ifdef notdef
63929649Ssam static
64030230Ssam ringinit(rp, size)
64130230Ssam 	register RING *rp;
64229649Ssam {
64329649Ssam 
64429649Ssam 	rp->r_rdidx = rp->r_wrtidx = 0;
64529649Ssam 	rp->r_size = size;
64629649Ssam }
64729649Ssam 
64829649Ssam static
649*30295Ssam ringfull(rp)
65030230Ssam 	register RING *rp;
65129649Ssam {
652*30295Ssam 	register short idx;
65330230Ssam 
654*30295Ssam 	idx = (rp->r_wrtidx + 1) & (rp->r_size-1);
655*30295Ssam 	return (idx == rp->r_rdidx);
65629649Ssam }
65729649Ssam 
65829649Ssam static
659*30295Ssam fir(rp)
66030230Ssam 	register RING *rp;
66129649Ssam {
66229649Ssam 
663*30295Ssam 	return (rp->r_rdidx != rp->r_wrtidx ? rp->r_slot[rp->r_rdidx] : 0);
66429649Ssam }
665*30295Ssam #endif
66629649Ssam 
66729649Ssam static
668*30295Ssam ringempty(rp)
669*30295Ssam 	register RING *rp;
670*30295Ssam {
671*30295Ssam 
672*30295Ssam 	return (rp->r_rdidx == rp->r_wrtidx);
673*30295Ssam }
674*30295Ssam 
675*30295Ssam static
67630230Ssam ringput(rp, v)
67730230Ssam 	register RING *rp;
678*30295Ssam 	BCB *v;
67929649Ssam {
68029649Ssam 	register int idx;
68129649Ssam 
68229649Ssam 	idx = (rp->r_wrtidx + 1) & (rp->r_size-1);
68330230Ssam 	if (idx != rp->r_rdidx) {
684*30295Ssam 		rp->r_slot[rp->r_wrtidx] = (int)v;
68529649Ssam 		rp->r_wrtidx = idx;
68630230Ssam 		if ((idx -= rp->r_rdidx) < 0)
68729649Ssam 			idx += rp->r_size;
68830230Ssam 		return (idx);			/* num ring entries */
68929649Ssam 	}
69030230Ssam 	return (0);
69129649Ssam }
69229649Ssam 
69329649Ssam static
69430230Ssam ringget(rp)
69530230Ssam 	register RING *rp;
69629649Ssam {
69729649Ssam 	register int i = 0;
69829649Ssam 
69930230Ssam 	if (rp->r_rdidx != rp->r_wrtidx) {
70030230Ssam 		i = rp->r_slot[rp->r_rdidx];
70129649Ssam 		rp->r_rdidx = (++rp->r_rdidx) & (rp->r_size-1);
70229649Ssam 	}
70330230Ssam 	return (i);
70429649Ssam }
70529649Ssam 
70630230Ssam /*
70730230Ssam  * ENP Ram device.
70830230Ssam  */
70930230Ssam enpr_open(dev)
71030230Ssam 	dev_t dev;
71129649Ssam {
71230230Ssam 	register int unit = ENPUNIT(dev);
71330230Ssam 	struct vba_device *ui;
71430230Ssam 	struct enpdevice *addr;
71529649Ssam 
71630230Ssam 	if (unit >= NENP || (ui = enpinfo[unit]) == 0 || ui->ui_alive == 0 ||
71730230Ssam 	    (addr = (struct enpdevice *)ui->ui_addr) == 0)
71830230Ssam 		return (ENODEV);
71930230Ssam 	if (addr->enp_state != S_ENPRESET)
72030230Ssam 		return (EACCES);  /* enp is not in reset state, don't open  */
72130230Ssam 	return (0);
72229649Ssam }
72329649Ssam 
724*30295Ssam /*ARGSUSED*/
72530230Ssam enpr_close(dev)
72630230Ssam 	dev_t dev;
72729649Ssam {
72829649Ssam 
72930230Ssam 	return (0);
73029649Ssam }
73129649Ssam 
73230230Ssam enpr_read(dev, uio)
73330230Ssam 	dev_t dev;
73430230Ssam 	register struct uio *uio;
73529649Ssam {
73630230Ssam 	register struct iovec *iov;
73730230Ssam 	struct enpdevice *addr;
73830230Ssam 	int error;
73929649Ssam 
74030230Ssam 	if (uio->uio_offset > RAM_SIZE)
74130230Ssam 		return (ENODEV);
742*30295Ssam 	iov = uio->uio_iov;
74330230Ssam 	if (uio->uio_offset + iov->iov_len > RAM_SIZE)
74430230Ssam 		iov->iov_len = RAM_SIZE - uio->uio_offset;
74530230Ssam 	addr = (struct enpdevice *)enpinfo[ENPUNIT(dev)]->ui_addr;
746*30295Ssam 	error = useracc(iov->iov_base, (unsigned)iov->iov_len, 0);
74730230Ssam 	if (error)
74830230Ssam 		return (error);
749*30295Ssam 	enpcopy((u_char *)&addr->enp_ram[uio->uio_offset],
750*30295Ssam 	    (u_char *)iov->iov_base, (u_int)iov->iov_len);
75130230Ssam 	uio->uio_resid -= iov->iov_len;
75230230Ssam 	iov->iov_len = 0;
75330230Ssam 	return (0);
75429649Ssam }
75529649Ssam 
75630230Ssam enpr_write(dev, uio)
75730230Ssam 	dev_t dev;
75830230Ssam 	register struct uio *uio;
75929649Ssam {
76030230Ssam 	register struct enpdevice *addr;
76130230Ssam 	register struct iovec *iov;
76230230Ssam 	register error;
76329649Ssam 
76430230Ssam 	addr = (struct enpdevice *)enpinfo[ENPUNIT(dev)]->ui_addr;
76530230Ssam 	iov = uio->uio_iov;
76630230Ssam 	if (uio->uio_offset > RAM_SIZE)
76730230Ssam 		return (ENODEV);
76830230Ssam 	if (uio->uio_offset + iov->iov_len > RAM_SIZE)
76930230Ssam 		iov->iov_len = RAM_SIZE - uio->uio_offset;
770*30295Ssam 	error =  useracc(iov->iov_base, (unsigned)iov->iov_len, 1);
77130230Ssam 	if (error)
77230230Ssam 		return (error);
773*30295Ssam 	enpcopy((u_char *)iov->iov_base,
774*30295Ssam 	    (u_char *)&addr->enp_ram[uio->uio_offset], (u_int)iov->iov_len);
77530230Ssam 	uio->uio_resid -= iov->iov_len;
77630230Ssam 	iov->iov_len = 0;
77730230Ssam 	return (0);
77829649Ssam }
77929649Ssam 
780*30295Ssam /*ARGSUSED*/
78130230Ssam enpr_ioctl(dev, cmd, data)
78230230Ssam 	dev_t dev;
78330230Ssam 	caddr_t data;
78429649Ssam {
78530230Ssam 	register struct enpdevice *addr;
78630230Ssam 	register unit = ENPUNIT(dev);
78729649Ssam 
78830230Ssam 	addr = (struct enpdevice *)enpinfo[unit]->ui_addr;
78930230Ssam 	switch(cmd) {
79030230Ssam 
79130230Ssam 	case ENPIOGO:
79230230Ssam /* not needed if prom based version */
79330230Ssam 		addr->enp_base = (int)addr;
79430230Ssam 		addr->enp_intrvec = enp_softc[unit].es_ivec;
79530230Ssam 		ENP_GO(addr, ENPSTART);
79630230Ssam 		DELAY(200000);
79730230Ssam 		enpinit(unit);
79830230Ssam 		addr->enp_state = S_ENPRUN;  /* it is running now */
79930230Ssam /* end of not needed */
80030230Ssam 		break;
80130230Ssam 
80230230Ssam 	case ENPIORESET:
80330230Ssam 		RESET_ENP(addr);
80430230Ssam 		addr->enp_state = S_ENPRESET;  /* it is reset now */
80530230Ssam 		DELAY(100000);
80630230Ssam 		break;
80729649Ssam 	}
80830230Ssam 	return (0);
80929649Ssam }
81029649Ssam #endif
811