From: curt@hoptoad.uucp (Curt Mayer)
Newsgroups: comp.unix.bsd
Subject: Shitty NE2000 NFS performance problem fixed right (source code)
Date: 18 Dec 92 21:20:25 GMT
Distribution: world
Organization: hardcore driver hackerz
Summary: buggy driver, RTFM

after playing with if_ne for a day or two, i have made 8K NFS mounts solid.
ftp performance is twice what the previous driver was. NFS is now filesystem
limited, the way it should be.

bad buffer management, coupled with not using a high performance mode the
ns8390, made the old driver lose bad when you got a lot of back to back
packets.

CAVEATS:
	ne1000 mode is untested, as I do not have one. I put the ne2000 clone
I have into 8 bit mode, and tested the 8 bit stuff. because of this, consider
this driver beta.

anyway, the new driver:

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	if_ne.c
#
echo x - if_ne.c
sed 's/^X//' >if_ne.c << 'END-of-if_ne.c'
X/*-
X * Copyright (c) 1990, 1991 William F. Jolitz.
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *	@(#)if_ne.c	7.4 (Berkeley) 5/21/91
X */
X
X/*
X * NE2000 Ethernet driver
X *
X * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
X * insight on the ne2000 gained from Robert Clements PC/FTP driver.
X *
X * receive bottom end totally rewritten by Curt Mayer, Dec 1992.
X * no longer loses back to back packets. 
X * note to driver writers: RTFM!
X */
X
X#include "ne.h"
X#if NNE > 0
X
X#include "param.h"
X#include "systm.h"
X#include "mbuf.h"
X#include "buf.h"
X#include "protosw.h"
X#include "socket.h"
X#include "ioctl.h"
X#include "errno.h"
X#include "syslog.h"
X
X#include "net/if.h"
X#include "net/netisr.h"
X#include "net/route.h"
X
X#ifdef INET
X#include "netinet/in.h"
X#include "netinet/in_systm.h"
X#include "netinet/in_var.h"
X#include "netinet/ip.h"
X#include "netinet/if_ether.h"
X#endif
X
X#ifdef NS
X#include "netns/ns.h"
X#include "netns/ns_if.h"
X#endif
X
X#include "i386/isa/isa_device.h"
X#include "i386/isa/if_nereg.h"
X#include "i386/isa/icu.h"
X
Xint	neprobe(), neattach(), neintr();
Xint	nestart(),neinit(), ether_output(), neioctl();
X
Xstruct	isa_driver nedriver = {
X	neprobe, neattach, "ne",
X};
X
Xstruct	mbuf *neget();
X
X#define ETHER_MIN_LEN 64
X#define ETHER_MAX_LEN 1536
X
X/*
X * Ethernet software status per interface.
X *
X * Each interface is referenced by a network interface structure,
X * ns_if, which the routing code uses to locate the interface.
X * This structure contains the output queue for the interface, its address, ...
X */
Xstruct	ne_softc {
X	struct	arpcom ns_ac;		/* Ethernet common part */
X#define	ns_if	ns_ac.ac_if		/* network-visible interface */
X#define	ns_addr	ns_ac.ac_enaddr		/* hardware Ethernet address */
X	int	ns_flags;
X#define	DSF_LOCK	1		/* block re-entering enstart */
X	int	ns_oactive;
X	int	ns_mask;
X	struct	prhdr	ns_ph;		/* hardware header of incoming packet*/
X	u_char	ns_pb[2048];
X	u_char	ns_txstart;		/* transmitter buffer start */
X	u_char	ns_rxstart;		/* receiver buffer start */
X	u_char	ns_rxend;		/* receiver buffer end */
X	short	ns_port;		/* i/o port base */
X	short	ns_mode;		/* word/byte mode */
X} ne_softc[NNE] ;
X#define	ENBUFSIZE	(sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)
X
X#define	PAT(n)	(0xa55a + 37*(n))
X
Xu_short boarddata[16];
X 
Xneprobe(dvp)
X	struct isa_device *dvp;
X{
X	int val, i, s, sum, pat;
X	register struct ne_softc *ns = &ne_softc[0];
X	register nec;
X
X#ifdef lint
X	neintr(0);
X#endif
X
X	nec = ns->ns_port = dvp->id_iobase;
X	s = splimp();
X
X	/* Byte Transfers, Burst Mode Select, Fifo at 8 bytes */
X	ns->ns_mode = DSDC_BMS|DSDC_FT1;
X	ns->ns_txstart = TBUF8 / DS_PGSIZE;
X	ns->ns_rxend = RBUFEND8 / DS_PGSIZE;
X
Xtryagain:
X
X	ns->ns_rxstart = ns->ns_txstart + (PKTSZ / DS_PGSIZE);
X
X	/* Reset the bastard */
X	val = inb(nec + ne_reset);
X	DELAY(200);
X	outb(nec + ne_reset, val);
X	DELAY(200);
X
X	outb(nec + ds_cmd, DSCM_STOP|DSCM_NODMA);
X	
X	i = 10000;
X	while ((inb(nec + ds0_isr) & DSIS_RESET) == 0 && i-- > 0);
X	if (i < 0) return (0);
X
X	outb(nec + ds0_isr, 0xff);
X	outb(nec + ds0_dcr, ns->ns_mode);
X	outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
X	DELAY(1000);
X
X	/* Check cmd reg and fail if not right */
X	if ((i = inb(nec + ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
X		return(0);
X
X	outb(nec + ds0_tcr, 0);
X	outb(nec + ds0_rcr, DSRC_MON);
X	outb(nec + ds0_pstart, ns->ns_rxstart);
X	outb(nec + ds0_pstop, ns->ns_rxend);
X	outb(nec + ds0_imr, 0);
X	outb(nec + ds0_isr, 0);
X	outb(nec + ds0_bnry, ns->ns_rxstart);
X	outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
X	outb(nec + ds1_curr, ns->ns_rxstart);
X	outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
X
X	/*
X	 * <groan> detect difference between units
X	 * solely by where the RAM is decoded.
X	 */
X	pat = PAT(0);
X	neput(ns, &pat, ns->ns_txstart * DS_PGSIZE, 4);
X	nefetch(ns, &pat, ns->ns_txstart * DS_PGSIZE, 4);
X	if (pat != PAT(0)) {
X		if (ns->ns_mode & DSDC_WTS)
X			return (0);
X
X		/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
X		ns->ns_mode = DSDC_WTS|DSDC_BMS|DSDC_FT1;
X		ns->ns_txstart = TBUF16 / DS_PGSIZE;
X		ns->ns_rxend = RBUFEND16 / DS_PGSIZE;
X		goto tryagain;
X	}
X
X
X	/* Extract board address */
X	nefetch (ns, (caddr_t)boarddata, 0, sizeof(boarddata));
X
X	for(i=0; i < 6; i++)
X		ns->ns_addr[i] = boarddata[i];
X	splx(s);
X	return (1);
X}
X
X/*
X * Fetch from onboard ROM/RAM
X */
Xnefetch (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
X	u_char cmd;
X	register nec = ns->ns_port;
X	int counter = 100000;
X
X	cmd = inb (nec + ds_cmd);
X	outb (nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
X
X	/* Setup remote dma */
X	outb (nec + ds0_isr, DSIS_RDC);
X
X	if ((ns->ns_mode & DSDC_WTS) && (len & 1))
X		len++;		/* roundup to words */
X
X	outb (nec+ds0_rbcr0, len);
X	outb (nec+ds0_rbcr1, len>>8);
X	outb (nec+ds0_rsar0, ad);
X	outb (nec+ds0_rsar1, ad>>8);
X
X	/* Execute & extract from card */
X	outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);
X
X	if (ns->ns_mode & DSDC_WTS)
X		insw (nec+ne_data, up, len/2);
X	else
X		insb (nec+ne_data, up, len);
X
X	/* Wait till done, then shutdown feature */
X	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
X		;
X	outb (nec+ds0_isr, DSIS_RDC);
X	outb (nec+ds_cmd, cmd);
X}
X
X/*
X * Put to onboard RAM
X */
Xneput (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
X	u_char cmd;
X	register nec = ns->ns_port;
X	int counter = 100000;
X
X	cmd = inb(nec+ds_cmd);
X	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
X
X	/* Setup for remote dma */
X	outb (nec+ds0_isr, DSIS_RDC);
X
X	if ((ns->ns_mode & DSDC_WTS) && (len & 1))
X		len++;		/* roundup to words */
X
X	outb (nec+ds0_rbcr0, len);
X	outb (nec+ds0_rbcr1, len>>8);
X	outb (nec+ds0_rsar0, ad);
X	outb (nec+ds0_rsar1, ad>>8);
X
X	/* Execute & stuff to card */
X	outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
X	if (ns->ns_mode & DSDC_WTS)
X		outsw (nec+ne_data, up, len/2);
X	else
X		outsb (nec+ne_data, up, len);
X	
X	/* Wait till done, then shutdown feature */
X	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
X		;
X	outb (nec+ds0_isr, DSIS_RDC);
X	outb (nec+ds_cmd, cmd);
X}
X
X/*
X * Reset of interface.
X */
Xnereset(unit, uban)
X	int unit, uban;
X{
X	if (unit >= NNE)
X		return;
X	printf("ne%d: reset\n", unit);
X	ne_softc[unit].ns_flags &= ~DSF_LOCK;
X	neinit(unit);
X}
X 
X/*
X * Interface exists: make available by filling in network interface
X * record.  System will initialize the interface when it is ready
X * to accept packets.  We get the ethernet address here.
X */
Xneattach(dvp)
X	struct isa_device *dvp;
X{
X	int unit = dvp->id_unit;
X	register struct ne_softc *ns = &ne_softc[unit];
X	register struct ifnet *ifp = &ns->ns_if;
X
X	ifp->if_unit = unit;
X	ifp->if_name = nedriver.name ;
X	ifp->if_mtu = ETHERMTU;
X	printf (" ne%d, address %s", 
X		(ns->ns_mode & DSDC_WTS) ? 2000 : 1000,
X		ether_sprintf(ns->ns_addr)) ;
X	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
X	ifp->if_init = neinit;
X	ifp->if_output = ether_output;
X	ifp->if_start = nestart;
X	ifp->if_ioctl = neioctl;
X	ifp->if_reset = nereset;
X	ifp->if_watchdog = 0;
X	if_attach(ifp);
X}
X
X/*
X * Initialization of interface; set up initialization block
X * and transmit/receive descriptor rings.
X */
Xneinit(unit)
X	int unit;
X{
X	register struct ne_softc *ns = &ne_softc[unit];
X	struct ifnet *ifp = &ns->ns_if;
X	int s;
X	int i; char *cp;
X	register nec = ns->ns_port;
X
X 	if (ifp->if_addrlist == (struct ifaddr *)0) return;
X	if (ifp->if_flags & IFF_RUNNING) return;
X
X	s = splimp();
X
X	/* set physical address on ethernet */
X	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
X	for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addr[i]);
X
X	/* clr logical address hash filter for now */
X	for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);
X
X	/* init regs */
X	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
X	outb (nec+ds0_rbcr0, 0);
X	outb (nec+ds0_rbcr1, 0);
X	outb (nec+ds0_imr, 0);
X	outb (nec+ds0_isr, 0xff);
X	outb(nec+ds0_dcr, ns->ns_mode);
X	outb(nec+ds0_tcr, 0);
X	outb (nec+ds0_rcr, DSRC_MON);
X	outb (nec+ds0_tpsr, 0);
X	outb(nec+ds0_pstart, ns->ns_rxstart);
X	outb(nec+ds0_pstop, ns->ns_rxend);
X	outb(nec+ds0_bnry, ns->ns_rxstart);
X	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
X	outb(nec+ds1_curr, ns->ns_rxstart);
X	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
X	outb (nec+ds0_rcr, DSRC_AB);
X	outb(nec+ds0_dcr, ns->ns_mode);
X	outb (nec+ds0_imr, 0xff);
X
X	ns->ns_if.if_flags |= IFF_RUNNING;
X	ns->ns_flags &= ~DSF_LOCK;
X	ns->ns_oactive = 0; ns->ns_mask = ~0;
X	nestart(ifp);
X	splx(s);
X}
X
X/*
X * Setup output on interface.
X * Get another datagram to send off of the interface queue,
X * and map it to the interface before starting the output.
X * called only at splimp or interrupt level.
X */
Xnestart(ifp)
X	struct ifnet *ifp;
X{
X	register struct ne_softc *ns = &ne_softc[ifp->if_unit];
X	struct mbuf *m0, *m;
X	int buffer;
X	int len = 0, i, total,t;
X	register nec = ns->ns_port;
X
X	/*
X	 * The DS8390 has only one transmit buffer, if it is busy we
X	 * must wait until the transmit interrupt completes.
X	 */
X	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
X
X	if (ns->ns_flags & DSF_LOCK)
X		return;
X
X	if (inb(nec+ds_cmd) & DSCM_TRANS)
X		return;
X
X	if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
X		return;
X
X	IF_DEQUEUE(&ns->ns_if.if_snd, m);
X 
X	if (m == 0)
X		return;
X
X	/*
X	 * Copy the mbuf chain into the transmit buffer
X	 */
X
X	ns->ns_flags |= DSF_LOCK;	/* prevent entering nestart */
X	buffer = ns->ns_txstart * DS_PGSIZE; 
X	len = i = 0;
X	t = 0;
X	for (m0 = m; m != 0; m = m->m_next)
X		t += m->m_len;
X		
X	m = m0;
X	total = t;
X	for (m0 = m; m != 0; ) {
X		
X		if (m->m_len&1 && t > m->m_len) {
X			neput(ns, mtod(m, caddr_t), buffer, m->m_len - 1);
X			t -= m->m_len - 1;
X			buffer += m->m_len - 1;
X			m->m_data += m->m_len - 1;
X			m->m_len = 1;
X			m = m_pullup(m, 2);
X		} else {
X			neput(ns, mtod(m, caddr_t), buffer, m->m_len);
X			buffer += m->m_len;
X			t -= m->m_len;
X			MFREE(m, m0);
X			m = m0;
X		}
X	}
X
X	/*
X	 * Init transmit length registers, and set transmit start flag.
X	 */
X
X	len = total;
X	if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
X	outb(nec+ds0_tbcr0,len&0xff);
X	outb(nec+ds0_tbcr1,(len>>8)&0xff);
X	outb(nec+ds0_tpsr, ns->ns_txstart);
X	outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
X}
X
X/*
X * Controller interrupt.
X */
Xneintr(unit)
X{
X	register struct ne_softc *ns = &ne_softc[unit];
X	u_char cmd,isr;
X	register nec = ns->ns_port;
X	u_char err;
X
X	/* Save cmd, clear interrupt */
X	cmd = inb (nec+ds_cmd);
Xloop:
X	isr = inb (nec+ds0_isr);
X	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
X	outb(nec+ds0_isr, isr);
X
X	/* Receiver error */
X	if (isr & DSIS_RXE) {
X		/* need to read these registers to clear status */
X		err = inb(nec+ ds0_rsr);
X		(void)inb(nec+0xD); 
X		(void)inb(nec+0xE); 
X		(void)inb(nec+0xF);
X		ns->ns_if.if_ierrors++;
X	}
X
X	/* We received something */
X	if (isr & DSIS_RX) {
X		u_char bnry;
X		u_char curr;
X		int len;
X		int i; 
X		unsigned char c;
X
X		while (1) {
X
X			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
X			bnry = inb(nec+ds0_bnry);
X			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
X			curr = inb(nec+ds1_curr); 
X
X#ifdef NEDEBUG
X			printf("neintr: bnry %x curr %x\n", bnry, curr);
X#endif
X
X			/* if ring empty, done! */
X			if (bnry == curr) {
X				break;
X			}
X
X			/* send packet with auto packet release */
X			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
X			outb (nec+ds0_rbcr1, 0x0f);
X			outb(nec+ds0_dcr, ns->ns_mode | DSDC_AR);
X			outb (nec+ds_cmd, DSCM_SENDP|DSCM_PG0|DSCM_START);
X
X			/* get length */
X			if ((ns->ns_mode & DSDC_WTS))
X				insw (nec+ne_data, (u_short *)&ns->ns_ph, 2);
X			else
X				insb (nec+ne_data, &ns->ns_ph, 4);
X
X#ifdef NEDEBUG
X			printf("neintr: sendp packet hdr: %x %x %x %x\n",
X				ns->ns_ph.pr_status,
X				ns->ns_ph.pr_nxtpg,
X				ns->ns_ph.pr_sz0,
X				ns->ns_ph.pr_sz1);
X#endif
X
X			ns->ns_if.if_ipackets++;
X			len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
X			if (len < ETHER_MIN_LEN || len > ETHER_MAX_LEN) {
X				printf("neintr: bnry %x curr %x\n", bnry, curr);
X				printf("neintr: packet hdr: %x %x %x %x\n",
X					ns->ns_ph.pr_status,
X					ns->ns_ph.pr_nxtpg,
X					ns->ns_ph.pr_sz0,
X					ns->ns_ph.pr_sz1);
X				printf("isr = 0x%x reg_isr=0x%x\n", 
X					isr, inb(nec+ds0_isr));
X				outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
X				bnry = inb(nec+ds0_bnry);
X				outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
X				curr = inb(nec+ds1_curr); 
X				printf("neintr: new bnry %x curr %x\n", bnry, curr);
X				printf("neintr: bad len %d\n-hanging-\n",
X					len);
X				while (1) ; 
X			}
X
X#ifdef NEDEBUG
X			printf("neintr: snarfing %d bytes\n", len);
X#endif
X
X			/* read packet */
X			if ((ns->ns_mode & DSDC_WTS)) {
X				insw (nec+ne_data, (u_short *)&ns->ns_pb, 
X					len / 2);
X				if (len & 1)
X					ns->ns_pb[len-1] = inb(nec+ne_data);
X			} else {
X				insb (nec+ne_data, ns->ns_pb, len);
X			}
X
X			if (!(inb (nec+ds0_isr) & DSIS_RDC)) {
X				printf("neintr: remote dma not done\nflushing...\n");
X				len = 0;
X				while (1) {
X					printf("%d %x\n", len, inb(nec+ne_data));
X					len++;
X				}
X			}
X
X			/* clear RDC */
X			outb (nec + ds0_isr, DSIS_RDC);
X
X			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
X
X			/* adjust for ether header and checksum */
X			len -= sizeof(struct ether_header) + sizeof(long);
X
X			/* process packet */
X			neread(ns,(caddr_t)(ns->ns_pb), len);
X		}
X	}
X
X	/* Transmit error */
X	if (isr & DSIS_TXE) {
X		ns->ns_flags &= ~DSF_LOCK;
X		/* Need to read these registers to clear status */
X		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
X		ns->ns_if.if_oerrors++;
X	}
X
X	/* Packet Transmitted */
X	if (isr & DSIS_TX) {
X		ns->ns_flags &= ~DSF_LOCK;
X		++ns->ns_if.if_opackets;
X		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
X	}
X
X	/* Receiver ovverun? */
X	if (isr & DSIS_ROVRN) {
X		log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr
X			/*, DSIS_BITS*/);
X		outb(nec+ds0_rbcr0, 0);
X		outb(nec+ds0_rbcr1, 0);
X		outb(nec+ds0_tcr, DSTC_LB0);
X		outb(nec+ds0_rcr, DSRC_MON);
X		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
X		outb(nec+ds0_rcr, DSRC_AB);
X		outb(nec+ds0_tcr, 0);
X	}
X
X	/* Any more to send? */
X	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
X	nestart(&ns->ns_if);
X	outb (nec+ds_cmd, cmd);
X	outb (nec+ds0_imr, 0xff);
X
X	/* Still more to do? */
X	isr = inb (nec+ds0_isr);
X	if(isr) goto loop;
X}
X
X/*
X * Pass a packet to the higher levels.
X * We deal with the trailer protocol here.
X */
Xneread(ns, buf, len)
X	register struct ne_softc *ns;
X	char *buf;
X	int len;
X{
X	register struct ether_header *eh;
X    	struct mbuf *m;
X	int off, resid;
X	register struct ifqueue *inq;
X
X	/*
X	 * Deal with trailer protocol: if type is trailer type
X	 * get true type from first 16-bit word past data.
X	 * Remember that type was trailer by setting off.
X	 */
X	eh = (struct ether_header *)buf;
X	eh->ether_type = ntohs((u_short)eh->ether_type);
X#define	nedataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
X	if (eh->ether_type >= ETHERTYPE_TRAIL &&
X	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
X		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
X		if (off >= ETHERMTU) return;		/* sanity */
X		eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *));
X		resid = ntohs(*(nedataaddr(eh, off+2, u_short *)));
X		if (off + resid > len) return;		/* sanity */
X		len = off + resid;
X	} else	off = 0;
X
X	if (len == 0) return;
X
X	/*
X	 * Pull packet off interface.  Off is nonzero if packet
X	 * has trailing header; neget will then force this header
X	 * information to be at the front, but we still have to drop
X	 * the type and length which are at the front of any trailer data.
X	 */
X	m = neget(buf, len, off, &ns->ns_if);
X	if (m == 0) return;
X
X	ether_input(&ns->ns_if, eh, m);
X}
X
X/*
X * Supporting routines
X */
X
X/*
X * Pull read data off a interface.
X * Len is length of data, with local net header stripped.
X * Off is non-zero if a trailer protocol was used, and
X * gives the offset of the trailer information.
X * We copy the trailer information and then all the normal
X * data into mbufs.  When full cluster sized units are present
X * we copy into clusters.
X */
Xstruct mbuf *
Xneget(buf, totlen, off0, ifp)
X	caddr_t buf;
X	int totlen, off0;
X	struct ifnet *ifp;
X{
X	struct mbuf *top, **mp, *m, *p;
X	int off = off0, len;
X	register caddr_t cp = buf;
X	char *epkt;
X
X	buf += sizeof(struct ether_header);
X	cp = buf;
X	epkt = cp + totlen;
X
X
X	if (off) {
X		cp += off + 2 * sizeof(u_short);
X		totlen -= 2 * sizeof(u_short);
X	}
X
X	MGETHDR(m, M_DONTWAIT, MT_DATA);
X	if (m == 0)
X		return (0);
X	m->m_pkthdr.rcvif = ifp;
X	m->m_pkthdr.len = totlen;
X	m->m_len = MHLEN;
X
X	top = 0;
X	mp = &top;
X	while (totlen > 0) {
X		if (top) {
X			MGET(m, M_DONTWAIT, MT_DATA);
X			if (m == 0) {
X				m_freem(top);
X				return (0);
X			}
X			m->m_len = MLEN;
X		}
X		len = min(totlen, epkt - cp);
X		if (len >= MINCLSIZE) {
X			MCLGET(m, M_DONTWAIT);
X			if (m->m_flags & M_EXT)
X				m->m_len = len = min(len, MCLBYTES);
X			else
X				len = m->m_len;
X		} else {
X			/*
X			 * Place initial small packet/header at end of mbuf.
X			 */
X			if (len < m->m_len) {
X				if (top == 0 && len + max_linkhdr <= m->m_len)
X					m->m_data += max_linkhdr;
X				m->m_len = len;
X			} else
X				len = m->m_len;
X		}
X		bcopy(cp, mtod(m, caddr_t), (unsigned)len);
X		cp += len;
X		*mp = m;
X		mp = &m->m_next;
X		totlen -= len;
X		if (cp == epkt)
X			cp = buf;
X	}
X	return (top);
X}
X
X/*
X * Process an ioctl request.
X */
Xneioctl(ifp, cmd, data)
X	register struct ifnet *ifp;
X	int cmd;
X	caddr_t data;
X{
X	register struct ifaddr *ifa = (struct ifaddr *)data;
X	struct ne_softc *ns = &ne_softc[ifp->if_unit];
X	struct ifreq *ifr = (struct ifreq *)data;
X	int s = splimp(), error = 0;
X
X
X	switch (cmd) {
X
X	case SIOCSIFADDR:
X		ifp->if_flags |= IFF_UP;
X
X		switch (ifa->ifa_addr->sa_family) {
X#ifdef INET
X		case AF_INET:
X			neinit(ifp->if_unit);	/* before arpwhohas */
X			((struct arpcom *)ifp)->ac_ipaddr =
X				IA_SIN(ifa)->sin_addr;
X			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
X			break;
X#endif
X#ifdef NS
X		case AF_NS:
X		    {
X			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
X
X			if (ns_nullhost(*ina))
X				ina->x_host = *(union ns_host *)(ns->ns_addr);
X			else {
X				/* 
X				 * The manual says we can't change the address 
X				 * while the receiver is armed,
X				 * so reset everything
X				 */
X				ifp->if_flags &= ~IFF_RUNNING; 
X				bcopy((caddr_t)ina->x_host.c_host,
X				    (caddr_t)ns->ns_addr, sizeof(ns->ns_addr));
X			}
X			neinit(ifp->if_unit); /* does ne_setaddr() */
X			break;
X		    }
X#endif
X		default:
X			neinit(ifp->if_unit);
X			break;
X		}
X		break;
X
X	case SIOCSIFFLAGS:
X		if ((ifp->if_flags & IFF_UP) == 0 &&
X		    ifp->if_flags & IFF_RUNNING) {
X			ifp->if_flags &= ~IFF_RUNNING;
X			outb(ns->ns_port + ds_cmd, DSCM_STOP|DSCM_NODMA);
X		} else if (ifp->if_flags & IFF_UP &&
X		    (ifp->if_flags & IFF_RUNNING) == 0)
X			neinit(ifp->if_unit);
X		break;
X
X#ifdef notdef
X	case SIOCGHWADDR:
X		bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
X			sizeof(ns->ns_addr));
X		break;
X#endif
X
X	default:
X		error = EINVAL;
X	}
X	splx(s);
X	return (error);
X}
X#endif
END-of-if_ne.c
exit
-- 
	curt mayer
        curt@toad.com
        415-387-0217 home