if_ie.c revision 147256
1581Srgrimes/*-
2581Srgrimes * Copyright (c) 1992, 1993, University of Vermont and State
3581Srgrimes *  Agricultural College.
4581Srgrimes * Copyright (c) 1992, 1993, Garrett A. Wollman.
5581Srgrimes *
6581Srgrimes * Portions:
7581Srgrimes * Copyright (c) 1990, 1991, William F. Jolitz
8581Srgrimes * Copyright (c) 1990, The Regents of the University of California
9581Srgrimes *
102281Swollman * 3Com 3C507 support:
112281Swollman * Copyright (c) 1993, 1994, Charles M. Hannum
122281Swollman *
1324909Sgibbs * EtherExpress 16 support:
1424909Sgibbs * Copyright (c) 1993, 1994, 1995, Rodney W. Grimes
1524909Sgibbs * Copyright (c) 1997, Aaron C. Smith
1624909Sgibbs *
17581Srgrimes * All rights reserved.
18581Srgrimes *
19581Srgrimes * Redistribution and use in source and binary forms, with or without
20581Srgrimes * modification, are permitted provided that the following conditions
21581Srgrimes * are met:
22581Srgrimes * 1. Redistributions of source code must retain the above copyright
23581Srgrimes *    notice, this list of conditions and the following disclaimer.
24581Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
25581Srgrimes *    notice, this list of conditions and the following disclaimer in the
26581Srgrimes *    documentation and/or other materials provided with the distribution.
27581Srgrimes * 3. All advertising materials mentioning features or use of this software
28581Srgrimes *    must display the following acknowledgement:
29581Srgrimes *	This product includes software developed by the University of
3024909Sgibbs *	Vermont and State Agricultural College and Garrett A. Wollman, by
3124909Sgibbs *	William F. Jolitz, by the University of California, Berkeley,
3224909Sgibbs *	Lawrence Berkeley Laboratory, and their contributors, by
3324909Sgibbs *	Charles M. Hannum, by Rodney W. Grimes, and by Aaron C. Smith.
34581Srgrimes * 4. Neither the names of the Universities nor the names of the authors
35581Srgrimes *    may be used to endorse or promote products derived from this software
36581Srgrimes *    without specific prior written permission.
37581Srgrimes *
38581Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39581Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40581Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41581Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR AUTHORS BE LIABLE
42581Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43581Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44581Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45581Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46581Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47581Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48581Srgrimes * SUCH DAMAGE.
49581Srgrimes *
5059812Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net>
51581Srgrimes */
52581Srgrimes
53119418Sobrien#include <sys/cdefs.h>
54119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ie/if_ie.c 147256 2005-06-10 16:49:24Z brooks $");
55119418Sobrien
56581Srgrimes/*
57581Srgrimes * Intel 82586 Ethernet chip
58581Srgrimes * Register, bit, and structure definitions.
59581Srgrimes *
60581Srgrimes * Written by GAW with reference to the Clarkson Packet Driver code for this
61581Srgrimes * chip written by Russ Nelson and others.
62581Srgrimes *
6326996Sgibbs * Intel EtherExpress 16 support from if_ix.c, written by Rodney W. Grimes.
64581Srgrimes */
65581Srgrimes
66581Srgrimes/*
67581Srgrimes * The i82586 is a very versatile chip, found in many implementations.
68581Srgrimes * Programming this chip is mostly the same, but certain details differ
698876Srgrimes * from card to card.  This driver is written so that different cards
7024909Sgibbs * can be automatically detected at run-time.
71581Srgrimes */
72581Srgrimes
73581Srgrimes/*
74112765Smdodd * Mode of operation:
75112765Smdodd *
76112765Smdodd * We run the 82586 in a standard Ethernet mode.  We keep NFRAMES
77112765Smdodd * received frame descriptors around for the receiver to use, and
78112765Smdodd * NRXBUFS associated receive buffer descriptors, both in a circular
79112765Smdodd * list.  Whenever a frame is received, we rotate both lists as
80112765Smdodd * necessary.  (The 586 treats both lists as a simple queue.)  We also
81112765Smdodd * keep a transmit command around so that packets can be sent off
82112765Smdodd * quickly.
83112765Smdodd *
84112765Smdodd * We configure the adapter in AL-LOC = 1 mode, which means that the
85112765Smdodd * Ethernet/802.3 MAC header is placed at the beginning of the receive
86112765Smdodd * buffer rather than being split off into various fields in the RFD.
87112765Smdodd * This also means that we must include this header in the transmit
88112765Smdodd * buffer as well.
89112765Smdodd *
90112765Smdodd * By convention, all transmit commands, and only transmit commands,
91112765Smdodd * shall have the I (IE_CMD_INTR) bit set in the command.  This way,
92112765Smdodd * when an interrupt arrives at ieintr(), it is immediately possible
93112765Smdodd * to tell what precisely caused it.  ANY OTHER command-sending routines
94112765Smdodd * should run at splimp(), and should post an acknowledgement to every
95112765Smdodd * interrupt they generate.
96112765Smdodd *
97112765Smdodd * The 82586 has a 24-bit address space internally, and the adaptor's
98112765Smdodd * memory is located at the top of this region.  However, the value
99112765Smdodd * we are given in configuration is normally the *bottom* of the adaptor
100112765Smdodd * RAM.  So, we must go through a few gyrations to come up with a
101112765Smdodd * kernel virtual address which represents the actual beginning of the
102112765Smdodd * 586 address space.  First, we autosize the RAM by running through
103112765Smdodd * several possible sizes and trying to initialize the adapter under
104112765Smdodd * the assumption that the selected size is correct.  Then, knowing
105112765Smdodd * the correct RAM size, we set up our pointers in the softc `iomem'
106112765Smdodd * represents the computed base of the 586 address space.  `iomembot'
107112765Smdodd * represents the actual configured base of adapter RAM.  Finally,
108112765Smdodd * `iosize' represents the calculated size of 586 RAM.  Then, when
109112765Smdodd * laying out commands, we use the interval [iomembot, iomembot +
110112765Smdodd * iosize); to make 24-pointers, we subtract iomem, and to make
111112765Smdodd * 16-pointers, we subtract iomem and and with 0xffff.
112112765Smdodd */
113581Srgrimes
1142056Swollman#include <sys/param.h>
1152056Swollman#include <sys/systm.h>
11650135Smsmith#include <sys/eventhandler.h>
11710080Sbde#include <sys/kernel.h>
11829024Sbde#include <sys/malloc.h>
1192056Swollman#include <sys/mbuf.h>
1202056Swollman#include <sys/socket.h>
12124204Sbde#include <sys/sockio.h>
1222056Swollman#include <sys/syslog.h>
123581Srgrimes
124112790Smdodd#include <sys/module.h>
125112790Smdodd#include <sys/bus.h>
126112790Smdodd
127112790Smdodd#include <machine/bus.h>
128112790Smdodd#include <machine/resource.h>
129112790Smdodd#include <sys/rman.h>
130112790Smdodd
13150026Smdodd#include <net/ethernet.h>
1322056Swollman#include <net/if.h>
1332056Swollman#include <net/if_types.h>
1342056Swollman#include <net/if_dl.h>
135581Srgrimes
13650026Smdodd#include <netinet/in.h>
13750026Smdodd#include <netinet/if_ether.h>
13850026Smdodd
13979077Simp#include <dev/ic/i82586.h>
140112790Smdodd#include <dev/ie/if_ievar.h>
14155953Speter#include <dev/ie/if_iereg.h>
14255953Speter#include <dev/ie/if_ie507.h>
14355953Speter#include <dev/ie/if_iee16.h>
1442268Sats#include <i386/isa/elink.h>
145581Srgrimes
1462056Swollman#include <net/bpf.h>
147581Srgrimes
148581Srgrimes#ifdef DEBUG
14926996Sgibbs#define IED_RINT	0x01
15026996Sgibbs#define IED_TINT	0x02
15126996Sgibbs#define IED_RNR		0x04
15226996Sgibbs#define IED_CNA		0x08
15326996Sgibbs#define IED_READFRAME	0x10
15433181Seivindstatic int	ie_debug = IED_RNR;
15526996Sgibbs
156581Srgrimes#endif
157581Srgrimes
15826996Sgibbs#define IE_BUF_LEN	ETHER_MAX_LEN	/* length of transmit buffer */
159581Srgrimes
160581Srgrimes/* Forward declaration */
161581Srgrimesstruct ie_softc;
162581Srgrimes
163112734Smdoddstatic void	ieinit			(void *);
164112734Smdoddstatic void	ie_stop			(struct ie_softc *);
165112734Smdoddstatic int	ieioctl			(struct ifnet *, u_long, caddr_t);
166112734Smdoddstatic void	iestart			(struct ifnet *);
16724909Sgibbs
168112734Smdoddstatic __inline void
169112734Smdodd		ee16_interrupt_enable	(struct ie_softc *);
170112734Smdoddstatic void	ee16_eeprom_outbits	(struct ie_softc *, int, int);
171112734Smdoddstatic void	ee16_eeprom_clock	(struct ie_softc *, int);
172112734Smdoddstatic u_short	ee16_read_eeprom	(struct ie_softc *, int);
173112734Smdoddstatic int	ee16_eeprom_inbits	(struct ie_softc *);
174112734Smdoddstatic void	ee16_shutdown		(void *, int);
17524909Sgibbs
176112734Smdoddstatic __inline void
177112734Smdodd		ie_ack			(struct ie_softc *, u_int);
178112734Smdoddstatic void	iereset			(struct ie_softc *);
179112734Smdoddstatic void	ie_readframe		(struct ie_softc *, int);
180112734Smdoddstatic void	ie_drop_packet_buffer	(struct ie_softc *);
181112734Smdoddstatic void	find_ie_mem_size	(struct ie_softc *);
182112734Smdoddstatic void	chan_attn_timeout	(void *);
183112734Smdoddstatic int	command_and_wait	(struct ie_softc *,
184112734Smdodd					 int, void volatile *, int);
185112734Smdoddstatic void	run_tdr			(struct ie_softc *,
186112734Smdodd					 volatile struct ie_tdr_cmd *);
187112734Smdoddstatic int	ierint			(struct ie_softc *);
188112734Smdoddstatic int	ietint			(struct ie_softc *);
189112734Smdoddstatic int	iernr			(struct ie_softc *);
190112734Smdoddstatic void	start_receiver		(struct ie_softc *);
191112734Smdoddstatic __inline int
192112734Smdodd		ieget			(struct ie_softc *, struct mbuf **);
193112734Smdoddstatic v_caddr_t setup_rfa		(struct ie_softc *, v_caddr_t);
194112734Smdoddstatic int	mc_setup		(struct ie_softc *);
195112734Smdoddstatic void	ie_mc_reset		(struct ie_softc *);
196581Srgrimes
197581Srgrimes#ifdef DEBUG
198112734Smdoddstatic void	print_rbd		(volatile struct ie_recv_buf_desc * rbd);
19933181Seivindstatic int	in_ierint = 0;
20033181Seivindstatic int	in_ietint = 0;
201581Srgrimes#endif
202581Srgrimes
20312724Sphkstatic const char *ie_hardware_names[] = {
204112790Smdodd	"None",
20526996Sgibbs	"StarLAN 10",
20626996Sgibbs	"EN100",
20726996Sgibbs	"StarLAN Fiber",
20826996Sgibbs	"3C507",
20926996Sgibbs	"NI5210",
21026996Sgibbs	"EtherExpress 16",
21126996Sgibbs	"Unknown"
212581Srgrimes};
213581Srgrimes
2148876Srgrimes/*
215112765Smdodd * sizeof(iscp) == 1+1+2+4 == 8
216112765Smdodd * sizeof(scb) == 2+2+2+2+2+2+2+2 == 16
217112765Smdodd * NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384
218112765Smdodd * sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18
219112765Smdodd * sizeof(transmit buffer) == 1512
220112765Smdodd * sizeof(transmit buffer desc) == 8
221112765Smdodd * -----
222112765Smdodd * 1946
223112765Smdodd *
224112765Smdodd * NRXBUFS * sizeof(rbd) == NRXBUFS*(2+2+4+2+2) == NRXBUFS*12
225112765Smdodd * NRXBUFS * IE_RBUF_SIZE == NRXBUFS*256
226112765Smdodd *
227112765Smdodd * NRXBUFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53
228112765Smdodd *
229112765Smdodd * With NRXBUFS == 48, this leaves us 1574 bytes for another command or
230112765Smdodd * more buffers.  Another transmit command would be 18+8+1512 == 1538
231112765Smdodd * ---just barely fits!
232112765Smdodd *
233112765Smdodd * Obviously all these would have to be reduced for smaller memory sizes.
234112765Smdodd * With a larger memory, it would be possible to roughly double the number
235112765Smdodd * of both transmit and receive buffers.
236112765Smdodd */
237581Srgrimes
238112783Smdodd#define	NFRAMES		4	/* number of receive frames */
239112783Smdodd#define	NRXBUFS		24	/* number of buffers to allocate */
240112783Smdodd#define	IE_RBUF_SIZE	256	/* size of each buffer, MUST BE POWER OF TWO */
241112783Smdodd#define	NTXBUFS		1	/* number of transmit commands */
242112783Smdodd#define	IE_TBUF_SIZE	ETHER_MAX_LEN	/* size of transmit buffer */
243581Srgrimes
24438232Sbde#define MK_24(base, ptr) ((caddr_t)((uintptr_t)ptr - (uintptr_t)base))
24538232Sbde#define MK_16(base, ptr) ((u_short)(uintptr_t)MK_24(base, ptr))
246581Srgrimes
24724909Sgibbsstatic void
248112734Smdoddee16_shutdown(void *xsc, int howto)
24924909Sgibbs{
250112734Smdodd	struct	ie_softc *sc = (struct ie_softc *)xsc;
25124909Sgibbs
252112734Smdodd	ee16_reset_586(sc);
253112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_ASIC);
254112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, 0);
25524909Sgibbs}
25624909Sgibbs
257581Srgrimes/*
258581Srgrimes * Taken almost exactly from Bill's if_is.c, then modified beyond recognition.
259581Srgrimes */
260112790Smdoddint
261112790Smdoddie_attach(device_t dev)
262581Srgrimes{
263112790Smdodd	struct ie_softc *       sc;
264112790Smdodd	struct ifnet *          ifp;
265112790Smdodd	size_t                  allocsize;
266112790Smdodd	int                     factor;
267581Srgrimes
268112790Smdodd	sc = device_get_softc(dev);
269147256Sbrooks	ifp = sc->ifp = if_alloc(IFT_ETHER);
270147256Sbrooks	if (ifp == NULL) {
271147256Sbrooks		device_printf(sc->dev, "can not if_alloc()\n");
272147256Sbrooks		return (ENOSPC);
273147256Sbrooks	}
27440565Sbde
275112790Smdodd	sc->dev = dev;
276112790Smdodd	sc->unit = device_get_unit(dev);
277112790Smdodd
27826996Sgibbs	/*
27926996Sgibbs	 * based on the amount of memory we have, allocate our tx and rx
28026996Sgibbs	 * resources.
28126996Sgibbs	 */
282112790Smdodd	factor = rman_get_size(sc->mem_res) / 8192;
283112734Smdodd	sc->nframes = factor * NFRAMES;
284112734Smdodd	sc->nrxbufs = factor * NRXBUFS;
285112734Smdodd	sc->ntxbufs = factor * NTXBUFS;
28625971Sgibbs
28726996Sgibbs	/*
28826996Sgibbs	 * Since all of these guys are arrays of pointers, allocate as one
28926996Sgibbs	 * big chunk and dole out accordingly.
29026996Sgibbs	 */
291112734Smdodd	allocsize = sizeof(void *) * (sc->nframes
292112734Smdodd				      + (sc->nrxbufs * 2)
293112734Smdodd				      + (sc->ntxbufs * 3));
294112734Smdodd	sc->rframes = (volatile struct ie_recv_frame_desc **) malloc(allocsize,
29526996Sgibbs								     M_DEVBUF,
29626996Sgibbs								   M_NOWAIT);
297147256Sbrooks	if (sc->rframes == NULL) {
298147256Sbrooks		if_free(ifp);
299112790Smdodd		return (ENXIO);
300147256Sbrooks	}
301112734Smdodd	sc->rbuffs =
302112734Smdodd	    (volatile struct ie_recv_buf_desc **)&sc->rframes[sc->nframes];
303112734Smdodd	sc->cbuffs = (volatile u_char **)&sc->rbuffs[sc->nrxbufs];
304112734Smdodd	sc->xmit_cmds =
305112734Smdodd	    (volatile struct ie_xmit_cmd **)&sc->cbuffs[sc->nrxbufs];
306112734Smdodd	sc->xmit_buffs =
307112734Smdodd	    (volatile struct ie_xmit_buf **)&sc->xmit_cmds[sc->ntxbufs];
308112734Smdodd	sc->xmit_cbuffs = (volatile u_char **)&sc->xmit_buffs[sc->ntxbufs];
30925971Sgibbs
310112790Smdodd	if (bootverbose)
311112790Smdodd		device_printf(sc->dev, "hardware type %s, revision %d\n",
312112790Smdodd			ie_hardware_names[sc->hard_type], sc->hard_vers + 1);
313112790Smdodd
314112734Smdodd	ifp->if_softc = sc;
315121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
31626996Sgibbs	ifp->if_mtu = ETHERMTU;
317133689Srwatson	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
318133689Srwatson	    IFF_NEEDSGIANT;
31926996Sgibbs	ifp->if_start = iestart;
32026996Sgibbs	ifp->if_ioctl = ieioctl;
32150084Smdodd	ifp->if_init = ieinit;
322112720Smdodd	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
3238876Srgrimes
324112734Smdodd	if (sc->hard_type == IE_EE16)
32550107Smsmith		EVENTHANDLER_REGISTER(shutdown_post_sync, ee16_shutdown,
326112734Smdodd				      sc, SHUTDOWN_PRI_DEFAULT);
32724909Sgibbs
328147256Sbrooks	ether_ifattach(ifp, sc->enaddr);
329112790Smdodd	return (0);
330581Srgrimes}
331581Srgrimes
332117877Sphkstatic __inline void
333117877Sphkie_ack(struct ie_softc *sc, u_int mask)
334117877Sphk{
335117877Sphk
336117877Sphk	sc->scb->ie_command = sc->scb->ie_status & mask;
337117877Sphk	(*sc->ie_chan_attn) (sc);
338117877Sphk}
339117877Sphk
340581Srgrimes/*
341581Srgrimes * What to do upon receipt of an interrupt.
342581Srgrimes */
343112790Smdoddvoid
344112790Smdoddie_intr(void *xsc)
345581Srgrimes{
346112790Smdodd	struct ie_softc *sc = (struct ie_softc *)xsc;
347112734Smdodd	u_short status;
348581Srgrimes
34926996Sgibbs	/* Clear the interrupt latch on the 3C507. */
350112734Smdodd	if (sc->hard_type == IE_3C507
351112734Smdodd	 && (inb(PORT(sc) + IE507_CTRL) & EL_CTRL_INTL))
352112734Smdodd		outb(PORT(sc) + IE507_ICTRL, 1);
35324909Sgibbs
35426996Sgibbs	/* disable interrupts on the EE16. */
355112734Smdodd	if (sc->hard_type == IE_EE16)
356112734Smdodd		outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded);
35724909Sgibbs
358112734Smdodd	status = sc->scb->ie_status;
359581Srgrimes
360581Srgrimesloop:
36125971Sgibbs
36226996Sgibbs	/* Don't ack interrupts which we didn't receive */
363112734Smdodd	ie_ack(sc, IE_ST_WHENCE & status);
36425971Sgibbs
36526996Sgibbs	if (status & (IE_ST_RECV | IE_ST_RNR)) {
366581Srgrimes#ifdef DEBUG
36726996Sgibbs		in_ierint++;
36826996Sgibbs		if (ie_debug & IED_RINT)
369112734Smdodd			printf("ie%d: rint\n", sc->unit);
370581Srgrimes#endif
371112734Smdodd		ierint(sc);
372581Srgrimes#ifdef DEBUG
37326996Sgibbs		in_ierint--;
374581Srgrimes#endif
37526996Sgibbs	}
37626996Sgibbs	if (status & IE_ST_DONE) {
377581Srgrimes#ifdef DEBUG
37826996Sgibbs		in_ietint++;
37926996Sgibbs		if (ie_debug & IED_TINT)
380112734Smdodd			printf("ie%d: tint\n", sc->unit);
381581Srgrimes#endif
382112734Smdodd		ietint(sc);
383581Srgrimes#ifdef DEBUG
38426996Sgibbs		in_ietint--;
385581Srgrimes#endif
38626996Sgibbs	}
38726996Sgibbs	if (status & IE_ST_RNR) {
388581Srgrimes#ifdef DEBUG
38926996Sgibbs		if (ie_debug & IED_RNR)
390112734Smdodd			printf("ie%d: rnr\n", sc->unit);
391581Srgrimes#endif
392112734Smdodd		iernr(sc);
39326996Sgibbs	}
394581Srgrimes#ifdef DEBUG
395112734Smdodd	if ((status & IE_ST_ALLDONE) && (ie_debug & IED_CNA))
396112734Smdodd		printf("ie%d: cna\n", sc->unit);
397581Srgrimes#endif
398581Srgrimes
399112734Smdodd	if ((status = sc->scb->ie_status) & IE_ST_WHENCE)
40026996Sgibbs		goto loop;
401581Srgrimes
40226996Sgibbs	/* Clear the interrupt latch on the 3C507. */
403112734Smdodd	if (sc->hard_type == IE_3C507)
404112734Smdodd		outb(PORT(sc) + IE507_ICTRL, 1);
40524909Sgibbs
40626996Sgibbs	/* enable interrupts on the EE16. */
407112734Smdodd	if (sc->hard_type == IE_EE16)
408112734Smdodd		outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE);
40924909Sgibbs
410581Srgrimes}
411581Srgrimes
412581Srgrimes/*
413581Srgrimes * Process a received-frame interrupt.
414581Srgrimes */
41524909Sgibbsstatic int
416112734Smdoddierint(struct ie_softc *sc)
417581Srgrimes{
41826996Sgibbs	int	i, status;
41926996Sgibbs	static int timesthru = 1024;
420581Srgrimes
421112734Smdodd	i = sc->rfhead;
42226996Sgibbs	while (1) {
423112734Smdodd		status = sc->rframes[i]->ie_fd_status;
424581Srgrimes
42526996Sgibbs		if ((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) {
426147256Sbrooks			sc->ifp->if_ipackets++;
42726996Sgibbs			if (!--timesthru) {
428147256Sbrooks				sc->ifp->if_ierrors +=
429112734Smdodd				    sc->scb->ie_err_crc +
430112734Smdodd				    sc->scb->ie_err_align +
431112734Smdodd				    sc->scb->ie_err_resource +
432112734Smdodd				    sc->scb->ie_err_overrun;
433112734Smdodd				sc->scb->ie_err_crc = 0;
434112734Smdodd				sc->scb->ie_err_align = 0;
435112734Smdodd				sc->scb->ie_err_resource = 0;
436112734Smdodd				sc->scb->ie_err_overrun = 0;
43726996Sgibbs				timesthru = 1024;
43826996Sgibbs			}
439112734Smdodd			ie_readframe(sc, i);
44026996Sgibbs		} else {
44126996Sgibbs			if (status & IE_FD_RNR) {
442112734Smdodd				if (!(sc->scb->ie_status & IE_RU_READY)) {
443112734Smdodd					sc->rframes[0]->ie_fd_next =
444112734Smdodd					    MK_16(MEM(sc), sc->rbuffs[0]);
445112734Smdodd					sc->scb->ie_recv_list =
446112734Smdodd					    MK_16(MEM(sc), sc->rframes[0]);
447112734Smdodd					command_and_wait(sc, IE_RU_START, 0, 0);
44826996Sgibbs				}
44926996Sgibbs			}
45026996Sgibbs			break;
45126996Sgibbs		}
452112734Smdodd		i = (i + 1) % sc->nframes;
453581Srgrimes	}
45426996Sgibbs	return (0);
455581Srgrimes}
456581Srgrimes
457581Srgrimes/*
458581Srgrimes * Process a command-complete interrupt.  These are only generated by
45926996Sgibbs * the transmission of frames.	This routine is deceptively simple, since
460581Srgrimes * most of the real work is done by iestart().
461581Srgrimes */
46226996Sgibbsstatic int
463112734Smdoddietint(struct ie_softc *sc)
464581Srgrimes{
46526996Sgibbs	int	status;
46626996Sgibbs	int	i;
467581Srgrimes
468147256Sbrooks	sc->ifp->if_timer = 0;
469147256Sbrooks	sc->ifp->if_flags &= ~IFF_OACTIVE;
470581Srgrimes
471112734Smdodd	for (i = 0; i < sc->xmit_count; i++) {
472112734Smdodd		status = sc->xmit_cmds[i]->ie_xmit_status;
473581Srgrimes
47426996Sgibbs		if (status & IE_XS_LATECOLL) {
475112734Smdodd			printf("ie%d: late collision\n", sc->unit);
476147256Sbrooks			sc->ifp->if_collisions++;
477147256Sbrooks			sc->ifp->if_oerrors++;
47826996Sgibbs		} else if (status & IE_XS_NOCARRIER) {
479112734Smdodd			printf("ie%d: no carrier\n", sc->unit);
480147256Sbrooks			sc->ifp->if_oerrors++;
48126996Sgibbs		} else if (status & IE_XS_LOSTCTS) {
482112734Smdodd			printf("ie%d: lost CTS\n", sc->unit);
483147256Sbrooks			sc->ifp->if_oerrors++;
48426996Sgibbs		} else if (status & IE_XS_UNDERRUN) {
485112734Smdodd			printf("ie%d: DMA underrun\n", sc->unit);
486147256Sbrooks			sc->ifp->if_oerrors++;
48726996Sgibbs		} else if (status & IE_XS_EXCMAX) {
488112734Smdodd			printf("ie%d: too many collisions\n", sc->unit);
489147256Sbrooks			sc->ifp->if_collisions += 16;
490147256Sbrooks			sc->ifp->if_oerrors++;
49126996Sgibbs		} else {
492147256Sbrooks			sc->ifp->if_opackets++;
493147256Sbrooks			sc->ifp->if_collisions += status & IE_XS_MAXCOLL;
49426996Sgibbs		}
49526996Sgibbs	}
496112734Smdodd	sc->xmit_count = 0;
497581Srgrimes
49826996Sgibbs	/*
49926996Sgibbs	 * If multicast addresses were added or deleted while we were
50026996Sgibbs	 * transmitting, ie_mc_reset() set the want_mcsetup flag indicating
50126996Sgibbs	 * that we should do it.
50226996Sgibbs	 */
503112734Smdodd	if (sc->want_mcsetup) {
504112734Smdodd		mc_setup(sc);
505112734Smdodd		sc->want_mcsetup = 0;
50626996Sgibbs	}
50726996Sgibbs	/* Wish I knew why this seems to be necessary... */
508112734Smdodd	sc->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL;
509581Srgrimes
510147256Sbrooks	iestart(sc->ifp);
51126996Sgibbs	return (0);		/* shouldn't be necessary */
512581Srgrimes}
513581Srgrimes
514581Srgrimes/*
515581Srgrimes * Process a receiver-not-ready interrupt.  I believe that we get these
516581Srgrimes * when there aren't enough buffers to go around.  For now (FIXME), we
517581Srgrimes * just restart the receiver, and hope everything's ok.
518581Srgrimes */
51926996Sgibbsstatic int
520112734Smdoddiernr(struct ie_softc *sc)
521581Srgrimes{
522581Srgrimes#ifdef doesnt_work
523112734Smdodd	setup_rfa(sc, (v_caddr_t) sc->rframes[0]);
524581Srgrimes
525112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
526112734Smdodd	command_and_wait(sc, IE_RU_START, 0, 0);
527581Srgrimes#else
52826996Sgibbs	/* This doesn't work either, but it doesn't hang either. */
529112734Smdodd	command_and_wait(sc, IE_RU_DISABLE, 0, 0);	/* just in case */
530112734Smdodd	setup_rfa(sc, (v_caddr_t) sc->rframes[0]);	/* ignore cast-qual */
531581Srgrimes
532112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
533112734Smdodd	command_and_wait(sc, IE_RU_START, 0, 0);	/* was ENABLE */
534581Srgrimes
535581Srgrimes#endif
536112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
537581Srgrimes
538147256Sbrooks	sc->ifp->if_ierrors++;
53926996Sgibbs	return (0);
540581Srgrimes}
541581Srgrimes
542581Srgrimes/*
543581Srgrimes * Compare two Ether/802 addresses for equality, inlined and
54426996Sgibbs * unrolled for speed.	I'd love to have an inline assembler
545581Srgrimes * version of this...
546581Srgrimes */
54735210Sbdestatic __inline int
54826996Sgibbsether_equal(u_char * one, u_char * two)
54926996Sgibbs{
55026996Sgibbs	if (one[0] != two[0])
55126996Sgibbs		return (0);
55226996Sgibbs	if (one[1] != two[1])
55326996Sgibbs		return (0);
55426996Sgibbs	if (one[2] != two[2])
55526996Sgibbs		return (0);
55626996Sgibbs	if (one[3] != two[3])
55726996Sgibbs		return (0);
55826996Sgibbs	if (one[4] != two[4])
55926996Sgibbs		return (0);
56026996Sgibbs	if (one[5] != two[5])
56126996Sgibbs		return (0);
56226996Sgibbs	return 1;
563581Srgrimes}
564581Srgrimes
565581Srgrimes/*
56660536Sarchie * Determine quickly whether we should bother reading in this packet.
56760536Sarchie * This depends on whether BPF and/or bridging is enabled, whether we
56860536Sarchie * are receiving multicast address, and whether promiscuous mode is enabled.
56960536Sarchie * We assume that if IFF_PROMISC is set, then *somebody* wants to see
57060536Sarchie * all incoming packets.
571581Srgrimes */
57235210Sbdestatic __inline int
573112734Smdoddcheck_eh(struct ie_softc *sc, struct ether_header *eh)
57426996Sgibbs{
57560536Sarchie	/* Optimize the common case: normal operation. We've received
57660536Sarchie	   either a unicast with our dest or a multicast packet. */
577112734Smdodd	if (sc->promisc == 0) {
57860536Sarchie		int i;
579581Srgrimes
58060536Sarchie		/* If not multicast, it's definitely for us */
58160536Sarchie		if ((eh->ether_dhost[0] & 1) == 0)
58226996Sgibbs			return (1);
583581Srgrimes
58460536Sarchie		/* Accept broadcasts (loose but fast check) */
58560536Sarchie		if (eh->ether_dhost[0] == 0xff)
58626996Sgibbs			return (1);
587581Srgrimes
58860536Sarchie		/* Compare against our multicast addresses */
589112734Smdodd		for (i = 0; i < sc->mcast_count; i++) {
59026996Sgibbs			if (ether_equal(eh->ether_dhost,
591112734Smdodd			    (u_char *)&sc->mcast_addrs[i]))
59226996Sgibbs				return (1);
59326996Sgibbs		}
59460536Sarchie		return (0);
59560536Sarchie	}
59660536Sarchie
59760536Sarchie	/* Always accept packets when in promiscuous mode */
598112734Smdodd	if ((sc->promisc & IFF_PROMISC) != 0)
59926996Sgibbs		return (1);
600581Srgrimes
60160536Sarchie	/* Always accept packets directed at us */
602147256Sbrooks	if (ether_equal(eh->ether_dhost, IFP2ENADDR(sc->ifp)))
60326996Sgibbs		return (1);
604581Srgrimes
60560536Sarchie	/* Must have IFF_ALLMULTI but not IFF_PROMISC set. The chip is
60660536Sarchie	   actually in promiscuous mode, so discard unicast packets. */
60760536Sarchie	return((eh->ether_dhost[0] & 1) != 0);
608581Srgrimes}
609581Srgrimes
610581Srgrimes/*
611581Srgrimes * We want to isolate the bits that have meaning...  This assumes that
612581Srgrimes * IE_RBUF_SIZE is an even power of two.  If somehow the act_len exceeds
613581Srgrimes * the size of the buffer, then we are screwed anyway.
614581Srgrimes */
61535210Sbdestatic __inline int
616112734Smdoddie_buflen(struct ie_softc *sc, int head)
61726996Sgibbs{
618112734Smdodd	return (sc->rbuffs[head]->ie_rbd_actual
61926996Sgibbs		& (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1)));
620581Srgrimes}
621581Srgrimes
62235210Sbdestatic __inline int
623112734Smdoddie_packet_len(struct ie_softc *sc)
62426996Sgibbs{
62526996Sgibbs	int	i;
626112734Smdodd	int	head = sc->rbhead;
62726996Sgibbs	int	acc = 0;
628581Srgrimes
62926996Sgibbs	do {
630112734Smdodd		if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
631581Srgrimes#ifdef DEBUG
632112734Smdodd			print_rbd(sc->rbuffs[sc->rbhead]);
633581Srgrimes#endif
63426996Sgibbs			log(LOG_ERR,
63526996Sgibbs			    "ie%d: receive descriptors out of sync at %d\n",
636112734Smdodd			    sc->unit, sc->rbhead);
637112734Smdodd			iereset(sc);
63826996Sgibbs			return (-1);
63926996Sgibbs		}
640112734Smdodd		i = sc->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST;
641581Srgrimes
642112734Smdodd		acc += ie_buflen(sc, head);
643112734Smdodd		head = (head + 1) % sc->nrxbufs;
64426996Sgibbs	} while (!i);
645581Srgrimes
64626996Sgibbs	return (acc);
647581Srgrimes}
648581Srgrimes
649581Srgrimes/*
650581Srgrimes * Read data off the interface, and turn it into an mbuf chain.
651581Srgrimes *
652581Srgrimes * This code is DRAMATICALLY different from the previous version; this
653581Srgrimes * version tries to allocate the entire mbuf chain up front, given the
654581Srgrimes * length of the data available.  This enables us to allocate mbuf
655581Srgrimes * clusters in many situations where before we would have had a long
656581Srgrimes * chain of partially-full mbufs.  This should help to speed up the
657581Srgrimes * operation considerably.  (Provided that it works, of course.)
658581Srgrimes */
65935210Sbdestatic __inline int
660112734Smdoddieget(struct ie_softc *sc, struct mbuf **mp)
661581Srgrimes{
662112720Smdodd	struct	ether_header eh;
66326996Sgibbs	struct	mbuf *m, *top, **mymp;
66426996Sgibbs	int	offset;
66526996Sgibbs	int	totlen, resid;
66626996Sgibbs	int	thismboff;
66726996Sgibbs	int	head;
668581Srgrimes
669112734Smdodd	totlen = ie_packet_len(sc);
67026996Sgibbs	if (totlen <= 0)
67126996Sgibbs		return (-1);
672581Srgrimes
67326996Sgibbs	/*
67426996Sgibbs	 * Snarf the Ethernet header.
67526996Sgibbs	 */
676112734Smdodd	bcopy((caddr_t)sc->cbuffs[sc->rbhead], &eh, sizeof(struct ether_header));
67726996Sgibbs	/* ignore cast-qual warning here */
678581Srgrimes
67926996Sgibbs	/*
68026996Sgibbs	 * As quickly as possible, check if this packet is for us. If not,
68126996Sgibbs	 * don't waste a single cycle copying the rest of the packet in.
68226996Sgibbs	 * This is only a consideration when FILTER is defined; i.e., when
68326996Sgibbs	 * we are either running BPF or doing multicasting.
68426996Sgibbs	 */
685112734Smdodd	if (!check_eh(sc, &eh)) {
686112734Smdodd		ie_drop_packet_buffer(sc);
687147256Sbrooks		sc->ifp->if_ierrors--;	/* just this case, it's not an
68826996Sgibbs						 * error
68926996Sgibbs						 */
69026996Sgibbs		return (-1);
69126996Sgibbs	}
692581Srgrimes
693112720Smdodd	MGETHDR(m, M_DONTWAIT, MT_DATA);
694112720Smdodd	if (!m) {
695112734Smdodd		ie_drop_packet_buffer(sc);
696112720Smdodd		/* XXXX if_ierrors++; */
697112720Smdodd		return (-1);
698112720Smdodd	}
699106937Ssam
700106937Ssam	*mp = m;
701147256Sbrooks	m->m_pkthdr.rcvif = sc->ifp;
702112720Smdodd	m->m_len = MHLEN;
703112720Smdodd	resid = m->m_pkthdr.len = totlen;
704112720Smdodd	top = 0;
705106937Ssam
70626996Sgibbs	mymp = &top;
707581Srgrimes
70826996Sgibbs	/*
70926996Sgibbs	 * This loop goes through and allocates mbufs for all the data we
71026996Sgibbs	 * will be copying in.	It does not actually do the copying yet.
71126996Sgibbs	 */
71226996Sgibbs	do {			/* while(resid > 0) */
71326996Sgibbs		/*
71426996Sgibbs		 * Try to allocate an mbuf to hold the data that we have.
71526996Sgibbs		 * If we already allocated one, just get another one and
71626996Sgibbs		 * stick it on the end (eventually).  If we don't already
71726996Sgibbs		 * have one, try to allocate an mbuf cluster big enough to
71826996Sgibbs		 * hold the whole packet, if we think it's reasonable, or a
71926996Sgibbs		 * single mbuf which may or may not be big enough. Got that?
72026996Sgibbs		 */
72126996Sgibbs		if (top) {
722111119Simp			MGET(m, M_DONTWAIT, MT_DATA);
72326996Sgibbs			if (!m) {
72426996Sgibbs				m_freem(top);
725112734Smdodd				ie_drop_packet_buffer(sc);
72626996Sgibbs				return (-1);
72726996Sgibbs			}
72826996Sgibbs			m->m_len = MLEN;
72926996Sgibbs		}
73026996Sgibbs		if (resid >= MINCLSIZE) {
731111119Simp			MCLGET(m, M_DONTWAIT);
73226996Sgibbs			if (m->m_flags & M_EXT)
73326996Sgibbs				m->m_len = min(resid, MCLBYTES);
73426996Sgibbs		} else {
73526996Sgibbs			if (resid < m->m_len) {
73626996Sgibbs				if (!top && resid + max_linkhdr <= m->m_len)
73726996Sgibbs					m->m_data += max_linkhdr;
73826996Sgibbs				m->m_len = resid;
73926996Sgibbs			}
74026996Sgibbs		}
74126996Sgibbs		resid -= m->m_len;
74226996Sgibbs		*mymp = m;
74326996Sgibbs		mymp = &m->m_next;
74426996Sgibbs	} while (resid > 0);
745581Srgrimes
746112720Smdodd	resid = totlen;					/* remaining data */
747112720Smdodd	offset = 0;					/* packet offset */
748112720Smdodd	thismboff = 0;					/* offset in m */
749112720Smdodd
750106937Ssam	m = top;					/* current mbuf */
751112734Smdodd	head = sc->rbhead;				/* current rx buffer */
752581Srgrimes
75326996Sgibbs	/*
75426996Sgibbs	 * Now we take the mbuf chain (hopefully only one mbuf most of the
75526996Sgibbs	 * time) and stuff the data into it.  There are no possible failures
75626996Sgibbs	 * at or after this point.
75726996Sgibbs	 */
75826996Sgibbs	while (resid > 0) {	/* while there's stuff left */
759112734Smdodd		int	thislen = ie_buflen(sc, head) - offset;
760581Srgrimes
76126996Sgibbs		/*
76226996Sgibbs		 * If too much data for the current mbuf, then fill the
76326996Sgibbs		 * current one up, go to the next one, and try again.
76426996Sgibbs		 */
76526996Sgibbs		if (thislen > m->m_len - thismboff) {
76626996Sgibbs			int	newlen = m->m_len - thismboff;
767581Srgrimes
768112734Smdodd			bcopy((v_caddr_t) (sc->cbuffs[head] + offset),
769112734Smdodd			      mtod(m, caddr_t) +thismboff, (unsigned) newlen);
77026996Sgibbs			/* ignore cast-qual warning */
77126996Sgibbs			m = m->m_next;
772106937Ssam			thismboff = 0;		/* new mbuf, so no offset */
77326996Sgibbs			offset += newlen;	/* we are now this far into
77426996Sgibbs						 * the packet */
77526996Sgibbs			resid -= newlen;	/* so there is this much left
77626996Sgibbs						 * to get */
77726996Sgibbs			continue;
77826996Sgibbs		}
77926996Sgibbs		/*
78026996Sgibbs		 * If there is more than enough space in the mbuf to hold
78126996Sgibbs		 * the contents of this buffer, copy everything in, advance
78226996Sgibbs		 * pointers, and so on.
78326996Sgibbs		 */
78426996Sgibbs		if (thislen < m->m_len - thismboff) {
785112734Smdodd			bcopy((v_caddr_t) (sc->cbuffs[head] + offset),
78626996Sgibbs			    mtod(m, caddr_t) +thismboff, (unsigned) thislen);
78726996Sgibbs			thismboff += thislen;	/* we are this far into the
78826996Sgibbs						 * mbuf */
78926996Sgibbs			resid -= thislen;	/* and this much is left */
79026996Sgibbs			goto nextbuf;
79126996Sgibbs		}
79226996Sgibbs		/*
79326996Sgibbs		 * Otherwise, there is exactly enough space to put this
79426996Sgibbs		 * buffer's contents into the current mbuf.  Do the
79526996Sgibbs		 * combination of the above actions.
79626996Sgibbs		 */
797112734Smdodd		bcopy((v_caddr_t) (sc->cbuffs[head] + offset),
79826996Sgibbs		      mtod(m, caddr_t) + thismboff, (unsigned) thislen);
79926996Sgibbs		m = m->m_next;
80026996Sgibbs		thismboff = 0;		/* new mbuf, start at the beginning */
80126996Sgibbs		resid -= thislen;	/* and we are this far through */
802581Srgrimes
80326996Sgibbs		/*
80426996Sgibbs		 * Advance all the pointers.  We can get here from either of
80526996Sgibbs		 * the last two cases, but never the first.
80626996Sgibbs		 */
807581Srgrimesnextbuf:
80826996Sgibbs		offset = 0;
809112734Smdodd		sc->rbuffs[head]->ie_rbd_actual = 0;
810112734Smdodd		sc->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST;
811112734Smdodd		sc->rbhead = head = (head + 1) % sc->nrxbufs;
812112734Smdodd		sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
813112734Smdodd		sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs;
81426996Sgibbs	}
815581Srgrimes
81626996Sgibbs	/*
81726996Sgibbs	 * Unless something changed strangely while we were doing the copy,
81826996Sgibbs	 * we have now copied everything in from the shared memory. This
81926996Sgibbs	 * means that we are done.
82026996Sgibbs	 */
82126996Sgibbs	return (0);
822581Srgrimes}
823581Srgrimes
824581Srgrimes/*
825581Srgrimes * Read frame NUM from unit UNIT (pre-cached as IE).
826581Srgrimes *
827581Srgrimes * This routine reads the RFD at NUM, and copies in the buffers from
828581Srgrimes * the list of RBD, then rotates the RBD and RFD lists so that the receiver
829581Srgrimes * doesn't start complaining.  Trailers are DROPPED---there's no point
83026996Sgibbs * in wasting time on confusing code to deal with them.	 Hopefully,
831581Srgrimes * this machine will never ARP for trailers anyway.
832581Srgrimes */
83326996Sgibbsstatic void
834112734Smdoddie_readframe(struct ie_softc *sc, int	num/* frame number to read */)
835581Srgrimes{
836147256Sbrooks	struct ifnet *ifp = sc->ifp;
83726996Sgibbs	struct ie_recv_frame_desc rfd;
83826996Sgibbs	struct mbuf *m = 0;
839106937Ssam#ifdef DEBUG
840106937Ssam	struct ether_header *eh;
841106937Ssam#endif
84226996Sgibbs
843112734Smdodd	bcopy((v_caddr_t) (sc->rframes[num]), &rfd,
84426996Sgibbs	      sizeof(struct ie_recv_frame_desc));
845581Srgrimes
84626996Sgibbs	/*
84726996Sgibbs	 * Immediately advance the RFD list, since we we have copied ours
84826996Sgibbs	 * now.
84926996Sgibbs	 */
850112734Smdodd	sc->rframes[num]->ie_fd_status = 0;
851112734Smdodd	sc->rframes[num]->ie_fd_last |= IE_FD_LAST;
852112734Smdodd	sc->rframes[sc->rftail]->ie_fd_last &= ~IE_FD_LAST;
853112734Smdodd	sc->rftail = (sc->rftail + 1) % sc->nframes;
854112734Smdodd	sc->rfhead = (sc->rfhead + 1) % sc->nframes;
855581Srgrimes
85626996Sgibbs	if (rfd.ie_fd_status & IE_FD_OK) {
857112734Smdodd		if (ieget(sc, &m)) {
858147256Sbrooks			sc->ifp->if_ierrors++;	/* this counts as an
85926996Sgibbs							 * error */
86026996Sgibbs			return;
86126996Sgibbs		}
86226996Sgibbs	}
863581Srgrimes#ifdef DEBUG
864106937Ssam	eh = mtod(m, struct ether_header *);
86526996Sgibbs	if (ie_debug & IED_READFRAME) {
866112734Smdodd		printf("ie%d: frame from ether %6D type %x\n", sc->unit,
867106937Ssam		       eh->ether_shost, ":", (unsigned) eh->ether_type);
86826996Sgibbs	}
869106937Ssam	if (ntohs(eh->ether_type) > ETHERTYPE_TRAIL
870106937Ssam	    && ntohs(eh->ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER))
87126996Sgibbs		printf("received trailer!\n");
872581Srgrimes#endif
873581Srgrimes
87426996Sgibbs	if (!m)
87526996Sgibbs		return;
876581Srgrimes
87726996Sgibbs	/*
87826996Sgibbs	 * Finally pass this packet up to higher layers.
87926996Sgibbs	 */
880106937Ssam	(*ifp->if_input)(ifp, m);
881581Srgrimes}
882581Srgrimes
88326996Sgibbsstatic void
884112734Smdoddie_drop_packet_buffer(struct ie_softc *sc)
88526996Sgibbs{
88626996Sgibbs	int	i;
887581Srgrimes
88826996Sgibbs	do {
88926996Sgibbs		/*
89026996Sgibbs		 * This means we are somehow out of sync.  So, we reset the
89126996Sgibbs		 * adapter.
89226996Sgibbs		 */
893112734Smdodd		if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
894581Srgrimes#ifdef DEBUG
895112734Smdodd			print_rbd(sc->rbuffs[sc->rbhead]);
896581Srgrimes#endif
89726996Sgibbs			log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n",
898112734Smdodd			    sc->unit, sc->rbhead);
899112734Smdodd			iereset(sc);
90026996Sgibbs			return;
90126996Sgibbs		}
902112734Smdodd		i = sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_LAST;
903581Srgrimes
904112734Smdodd		sc->rbuffs[sc->rbhead]->ie_rbd_length |= IE_RBD_LAST;
905112734Smdodd		sc->rbuffs[sc->rbhead]->ie_rbd_actual = 0;
906112734Smdodd		sc->rbhead = (sc->rbhead + 1) % sc->nrxbufs;
907112734Smdodd		sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
908112734Smdodd		sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs;
90926996Sgibbs	} while (!i);
910581Srgrimes}
911581Srgrimes
912581Srgrimes
913581Srgrimes/*
914581Srgrimes * Start transmission on an interface.
915581Srgrimes */
916798Swollmanstatic void
91726996Sgibbsiestart(struct ifnet *ifp)
918581Srgrimes{
919112734Smdodd	struct	 ie_softc *sc = ifp->if_softc;
92026996Sgibbs	struct	 mbuf *m0, *m;
92143314Sdillon	volatile unsigned char *buffer;
92226996Sgibbs	u_short	 len;
923581Srgrimes
92426996Sgibbs	/*
92526996Sgibbs	 * This is not really volatile, in this routine, but it makes gcc
92626996Sgibbs	 * happy.
92726996Sgibbs	 */
928112734Smdodd	volatile u_short *bptr = &sc->scb->ie_command_list;
929581Srgrimes
93026996Sgibbs	if (!(ifp->if_flags & IFF_RUNNING))
93126996Sgibbs		return;
93226996Sgibbs	if (ifp->if_flags & IFF_OACTIVE)
93326996Sgibbs		return;
934581Srgrimes
93526996Sgibbs	do {
936147256Sbrooks		IF_DEQUEUE(&sc->ifp->if_snd, m);
93726996Sgibbs		if (!m)
93826996Sgibbs			break;
939581Srgrimes
940112734Smdodd		buffer = sc->xmit_cbuffs[sc->xmit_count];
94126996Sgibbs		len = 0;
942581Srgrimes
94326996Sgibbs		for (m0 = m; m && len < IE_BUF_LEN; m = m->m_next) {
94426996Sgibbs			bcopy(mtod(m, caddr_t), buffer, m->m_len);
94526996Sgibbs			buffer += m->m_len;
94626996Sgibbs			len += m->m_len;
94726996Sgibbs		}
948581Srgrimes
94926996Sgibbs		m_freem(m0);
95026996Sgibbs		len = max(len, ETHER_MIN_LEN);
95126996Sgibbs
95226996Sgibbs		/*
95326996Sgibbs		 * See if bpf is listening on this interface, let it see the
95426996Sgibbs		 * packet before we commit it to the wire.
95526996Sgibbs		 */
956147256Sbrooks		BPF_TAP(sc->ifp,
957112734Smdodd			(void *)sc->xmit_cbuffs[sc->xmit_count], len);
958581Srgrimes
959112734Smdodd		sc->xmit_buffs[sc->xmit_count]->ie_xmit_flags =
96026996Sgibbs		    IE_XMIT_LAST|len;
961112734Smdodd		sc->xmit_buffs[sc->xmit_count]->ie_xmit_next = 0xffff;
962112734Smdodd		sc->xmit_buffs[sc->xmit_count]->ie_xmit_buf =
963112734Smdodd		    MK_24(sc->iomem, sc->xmit_cbuffs[sc->xmit_count]);
964581Srgrimes
965112734Smdodd		sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT;
966112734Smdodd		sc->xmit_cmds[sc->xmit_count]->ie_xmit_status = 0;
967112734Smdodd		sc->xmit_cmds[sc->xmit_count]->ie_xmit_desc =
968112734Smdodd		    MK_16(sc->iomem, sc->xmit_buffs[sc->xmit_count]);
969581Srgrimes
970112734Smdodd		*bptr = MK_16(sc->iomem, sc->xmit_cmds[sc->xmit_count]);
971112734Smdodd		bptr = &sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_link;
972112734Smdodd		sc->xmit_count++;
973112734Smdodd	} while (sc->xmit_count < sc->ntxbufs);
974581Srgrimes
97526996Sgibbs	/*
97626996Sgibbs	 * If we queued up anything for transmission, send it.
97726996Sgibbs	 */
978112734Smdodd	if (sc->xmit_count) {
979112734Smdodd		sc->xmit_cmds[sc->xmit_count - 1]->com.ie_cmd_cmd |=
98026996Sgibbs		    IE_CMD_LAST | IE_CMD_INTR;
981581Srgrimes
98226996Sgibbs		/*
98326996Sgibbs		 * By passing the command pointer as a null, we tell
98426996Sgibbs		 * command_and_wait() to pretend that this isn't an action
98526996Sgibbs		 * command.  I wish I understood what was happening here.
98626996Sgibbs		 */
987112734Smdodd		command_and_wait(sc, IE_CU_START, 0, 0);
98826996Sgibbs		ifp->if_flags |= IFF_OACTIVE;
98926996Sgibbs	}
99026996Sgibbs	return;
991581Srgrimes}
992581Srgrimes
993581Srgrimes/*
994581Srgrimes * Check to see if there's an 82586 out there.
995581Srgrimes */
996112790Smdoddint
997112790Smdoddcheck_ie_present(struct ie_softc *sc)
998581Srgrimes{
99926996Sgibbs	volatile struct ie_sys_conf_ptr *scp;
100026996Sgibbs	volatile struct ie_int_sys_conf_ptr *iscp;
100126996Sgibbs	volatile struct ie_sys_ctl_block *scb;
100226996Sgibbs	u_long	realbase;
100326996Sgibbs	int	s;
1004581Srgrimes
100526996Sgibbs	s = splimp();
1006581Srgrimes
1007112790Smdodd	realbase = (uintptr_t) sc->iomembot + sc->iosize  - (1 << 24);
1008581Srgrimes
100938232Sbde	scp = (volatile struct ie_sys_conf_ptr *) (uintptr_t)
101038232Sbde	      (realbase + IE_SCP_ADDR);
101143314Sdillon	bzero((volatile char *) scp, sizeof *scp);
10128876Srgrimes
101326996Sgibbs	/*
101426996Sgibbs	 * First we put the ISCP at the bottom of memory; this tests to make
101526996Sgibbs	 * sure that our idea of the size of memory is the same as the
101626996Sgibbs	 * controller's. This is NOT where the ISCP will be in normal
101726996Sgibbs	 * operation.
101826996Sgibbs	 */
1019112790Smdodd	iscp = (volatile struct ie_int_sys_conf_ptr *) sc->iomembot;
102043314Sdillon	bzero((volatile char *)iscp, sizeof *iscp);
1021581Srgrimes
1022112790Smdodd	scb = (volatile struct ie_sys_ctl_block *) sc->iomembot;
102343314Sdillon	bzero((volatile char *)scb, sizeof *scb);
1024581Srgrimes
1025112734Smdodd	scp->ie_bus_use = sc->bus_use;	/* 8-bit or 16-bit */
102647108Sbde	scp->ie_iscp_ptr = (caddr_t) (uintptr_t)
102747108Sbde	    ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase);
1028581Srgrimes
102926996Sgibbs	iscp->ie_busy = 1;
103026996Sgibbs	iscp->ie_scb_offset = MK_16(realbase, scb) + 256;
1031581Srgrimes
1032112734Smdodd	(*sc->ie_reset_586) (sc);
1033112734Smdodd	(*sc->ie_chan_attn) (sc);
1034581Srgrimes
103526996Sgibbs	DELAY(100);		/* wait a while... */
1036581Srgrimes
103726996Sgibbs	if (iscp->ie_busy) {
103826996Sgibbs		splx(s);
103926996Sgibbs		return (0);
104026996Sgibbs	}
104126996Sgibbs	/*
104226996Sgibbs	 * Now relocate the ISCP to its real home, and reset the controller
104326996Sgibbs	 * again.
104426996Sgibbs	 */
104538232Sbde	iscp = (void *) Align((caddr_t) (uintptr_t)
104638232Sbde			      (realbase + IE_SCP_ADDR -
104738232Sbde			       sizeof(struct ie_int_sys_conf_ptr)));
104843314Sdillon	bzero((volatile char *) iscp, sizeof *iscp);	/* ignore cast-qual */
1049581Srgrimes
105047108Sbde	scp->ie_iscp_ptr = (caddr_t) (uintptr_t)
105147108Sbde	    ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase);
1052581Srgrimes
105326996Sgibbs	iscp->ie_busy = 1;
105426996Sgibbs	iscp->ie_scb_offset = MK_16(realbase, scb);
1055581Srgrimes
1056112734Smdodd	(*sc->ie_reset_586) (sc);
1057112734Smdodd	(*sc->ie_chan_attn) (sc);
1058581Srgrimes
105926996Sgibbs	DELAY(100);
1060581Srgrimes
106126996Sgibbs	if (iscp->ie_busy) {
106226996Sgibbs		splx(s);
106326996Sgibbs		return (0);
106426996Sgibbs	}
1065112734Smdodd	sc->iomem = (caddr_t) (uintptr_t) realbase;
1066581Srgrimes
1067112734Smdodd	sc->iscp = iscp;
1068112734Smdodd	sc->scb = scb;
1069581Srgrimes
107026996Sgibbs	/*
107126996Sgibbs	 * Acknowledge any interrupts we may have caused...
107226996Sgibbs	 */
1073112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
107426996Sgibbs	splx(s);
1075581Srgrimes
107626996Sgibbs	return (1);
1077581Srgrimes}
1078581Srgrimes
1079581Srgrimes/*
1080581Srgrimes * Divine the memory size of ie board UNIT.
1081581Srgrimes * Better hope there's nothing important hiding just below the ie card...
1082581Srgrimes */
108326996Sgibbsstatic void
1084112734Smdoddfind_ie_mem_size(struct ie_softc *sc)
1085581Srgrimes{
108626996Sgibbs	unsigned size;
1087581Srgrimes
1088112734Smdodd	sc->iosize = 0;
1089581Srgrimes
109026996Sgibbs	for (size = 65536; size >= 8192; size -= 8192) {
1091112790Smdodd		if (check_ie_present(sc)) {
109226996Sgibbs			return;
109326996Sgibbs		}
109426996Sgibbs	}
1095581Srgrimes
109626996Sgibbs	return;
1097581Srgrimes}
1098581Srgrimes
1099112790Smdoddvoid
1100112734Smdoddel_reset_586(struct ie_softc *sc)
11012268Sats{
1102112734Smdodd	outb(PORT(sc) + IE507_CTRL, EL_CTRL_RESET);
110326996Sgibbs	DELAY(100);
1104112734Smdodd	outb(PORT(sc) + IE507_CTRL, EL_CTRL_NORMAL);
110526996Sgibbs	DELAY(100);
11062268Sats}
11072268Sats
1108112790Smdoddvoid
1109112734Smdoddsl_reset_586(struct ie_softc *sc)
1110581Srgrimes{
1111112734Smdodd	outb(PORT(sc) + IEATT_RESET, 0);
1112581Srgrimes}
1113581Srgrimes
1114112790Smdoddvoid
1115112734Smdoddee16_reset_586(struct ie_softc *sc)
111624909Sgibbs{
1117112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_586);
111824909Sgibbs	DELAY(100);
1119112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, 0);
112024909Sgibbs	DELAY(100);
112124909Sgibbs}
112224909Sgibbs
1123112790Smdoddvoid
1124112734Smdoddel_chan_attn(struct ie_softc *sc)
11252268Sats{
1126112734Smdodd	outb(PORT(sc) + IE507_ATTN, 1);
11272268Sats}
11282268Sats
1129112790Smdoddvoid
1130112734Smdoddsl_chan_attn(struct ie_softc *sc)
1131581Srgrimes{
1132112734Smdodd	outb(PORT(sc) + IEATT_ATTN, 0);
1133581Srgrimes}
1134581Srgrimes
1135112790Smdoddvoid
1136112734Smdoddee16_chan_attn(struct ie_softc *sc)
113724909Sgibbs{
1138112734Smdodd	outb(PORT(sc) + IEE16_ATTN, 0);
113924909Sgibbs}
114024909Sgibbs
1141112790Smdoddu_short
114226996Sgibbsee16_read_eeprom(struct ie_softc *sc, int location)
114324909Sgibbs{
114426996Sgibbs	int	ectrl, edata;
114524909Sgibbs
114624909Sgibbs	ectrl = inb(sc->port + IEE16_ECTRL);
114724909Sgibbs	ectrl &= IEE16_ECTRL_MASK;
114824909Sgibbs	ectrl |= IEE16_ECTRL_EECS;
114924909Sgibbs	outb(sc->port + IEE16_ECTRL, ectrl);
115024909Sgibbs
115124909Sgibbs	ee16_eeprom_outbits(sc, IEE16_EEPROM_READ, IEE16_EEPROM_OPSIZE1);
115224909Sgibbs	ee16_eeprom_outbits(sc, location, IEE16_EEPROM_ADDR_SIZE);
115324909Sgibbs	edata = ee16_eeprom_inbits(sc);
115424909Sgibbs	ectrl = inb(sc->port + IEE16_ECTRL);
115524909Sgibbs	ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EEDI | IEE16_ECTRL_EECS);
115624909Sgibbs	outb(sc->port + IEE16_ECTRL, ectrl);
115724909Sgibbs	ee16_eeprom_clock(sc, 1);
115824909Sgibbs	ee16_eeprom_clock(sc, 0);
115924909Sgibbs	return edata;
116024909Sgibbs}
116124909Sgibbs
1162104094Sphkstatic void
116326996Sgibbsee16_eeprom_outbits(struct ie_softc *sc, int edata, int count)
116424909Sgibbs{
116526996Sgibbs	int	ectrl, i;
116624909Sgibbs
116724909Sgibbs	ectrl = inb(sc->port + IEE16_ECTRL);
116824909Sgibbs	ectrl &= ~IEE16_RESET_ASIC;
116924909Sgibbs	for (i = count - 1; i >= 0; i--) {
117024909Sgibbs		ectrl &= ~IEE16_ECTRL_EEDI;
117124909Sgibbs		if (edata & (1 << i)) {
117224909Sgibbs			ectrl |= IEE16_ECTRL_EEDI;
117324909Sgibbs		}
117424909Sgibbs		outb(sc->port + IEE16_ECTRL, ectrl);
117524909Sgibbs		DELAY(1);	/* eeprom data must be setup for 0.4 uSec */
117624909Sgibbs		ee16_eeprom_clock(sc, 1);
117724909Sgibbs		ee16_eeprom_clock(sc, 0);
117824909Sgibbs	}
117924909Sgibbs	ectrl &= ~IEE16_ECTRL_EEDI;
118024909Sgibbs	outb(sc->port + IEE16_ECTRL, ectrl);
118124909Sgibbs	DELAY(1);		/* eeprom data must be held for 0.4 uSec */
118224909Sgibbs}
118324909Sgibbs
1184104094Sphkstatic int
118526996Sgibbsee16_eeprom_inbits(struct ie_softc *sc)
118624909Sgibbs{
118726996Sgibbs	int	ectrl, edata, i;
118824909Sgibbs
118924909Sgibbs	ectrl = inb(sc->port + IEE16_ECTRL);
119024909Sgibbs	ectrl &= ~IEE16_RESET_ASIC;
119124909Sgibbs	for (edata = 0, i = 0; i < 16; i++) {
119224909Sgibbs		edata = edata << 1;
119324909Sgibbs		ee16_eeprom_clock(sc, 1);
119424909Sgibbs		ectrl = inb(sc->port + IEE16_ECTRL);
119524909Sgibbs		if (ectrl & IEE16_ECTRL_EEDO) {
119624909Sgibbs			edata |= 1;
119724909Sgibbs		}
119824909Sgibbs		ee16_eeprom_clock(sc, 0);
119924909Sgibbs	}
120024909Sgibbs	return (edata);
120124909Sgibbs}
120224909Sgibbs
1203104094Sphkstatic void
120426996Sgibbsee16_eeprom_clock(struct ie_softc *sc, int state)
120524909Sgibbs{
120626996Sgibbs	int	ectrl;
120724909Sgibbs
120824909Sgibbs	ectrl = inb(sc->port + IEE16_ECTRL);
120924909Sgibbs	ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EESK);
121024909Sgibbs	if (state) {
121124909Sgibbs		ectrl |= IEE16_ECTRL_EESK;
121224909Sgibbs	}
121324909Sgibbs	outb(sc->port + IEE16_ECTRL, ectrl);
121424909Sgibbs	DELAY(9);		/* EESK must be stable for 8.38 uSec */
121524909Sgibbs}
121624909Sgibbs
121735210Sbdestatic __inline void
121826996Sgibbsee16_interrupt_enable(struct ie_softc *sc)
121924909Sgibbs{
122024909Sgibbs	DELAY(100);
122124909Sgibbs	outb(sc->port + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE);
122224909Sgibbs	DELAY(100);
122324909Sgibbs}
122424909Sgibbs
1225112790Smdoddvoid
1226112734Smdoddsl_read_ether(struct ie_softc *sc, unsigned char *addr)
1227581Srgrimes{
122826996Sgibbs	int	i;
1229581Srgrimes
123026996Sgibbs	for (i = 0; i < 6; i++)
1231112734Smdodd		addr[i] = inb(PORT(sc) + i);
1232581Srgrimes}
1233581Srgrimes
1234798Swollmanstatic void
1235112734Smdoddiereset(struct ie_softc *sc)
1236581Srgrimes{
123726996Sgibbs	int	s = splimp();
1238581Srgrimes
1239112734Smdodd	printf("ie%d: reset\n", sc->unit);
1240147256Sbrooks	sc->ifp->if_flags &= ~IFF_UP;
1241147256Sbrooks	ieioctl(sc->ifp, SIOCSIFFLAGS, 0);
1242581Srgrimes
124326996Sgibbs	/*
124426996Sgibbs	 * Stop i82586 dead in its tracks.
124526996Sgibbs	 */
1246112734Smdodd	if (command_and_wait(sc, IE_RU_ABORT | IE_CU_ABORT, 0, 0))
1247112734Smdodd		printf("ie%d: abort commands timed out\n", sc->unit);
1248581Srgrimes
1249112734Smdodd	if (command_and_wait(sc, IE_RU_DISABLE | IE_CU_STOP, 0, 0))
1250112734Smdodd		printf("ie%d: disable commands timed out\n", sc->unit);
1251581Srgrimes
1252581Srgrimes#ifdef notdef
1253112790Smdodd	if (!check_ie_present(sc))
125426996Sgibbs		panic("ie disappeared!");
1255581Srgrimes#endif
1256581Srgrimes
1257147256Sbrooks	sc->ifp->if_flags |= IFF_UP;
1258147256Sbrooks	ieioctl(sc->ifp, SIOCSIFFLAGS, 0);
12598876Srgrimes
126026996Sgibbs	splx(s);
126126996Sgibbs	return;
1262581Srgrimes}
1263581Srgrimes
1264581Srgrimes/*
1265581Srgrimes * This is called if we time out.
1266581Srgrimes */
1267798Swollmanstatic void
126826996Sgibbschan_attn_timeout(void *rock)
1269581Srgrimes{
127026996Sgibbs	*(int *) rock = 1;
1271581Srgrimes}
1272581Srgrimes
1273581Srgrimes/*
1274581Srgrimes * Send a command to the controller and wait for it to either
1275581Srgrimes * complete or be accepted, depending on the command.  If the
1276581Srgrimes * command pointer is null, then pretend that the command is
1277581Srgrimes * not an action command.  If the command pointer is not null,
1278581Srgrimes * and the command is an action command, wait for
1279581Srgrimes * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK
1280581Srgrimes * to become true.
1281581Srgrimes */
128226996Sgibbsstatic int
1283112734Smdoddcommand_and_wait(struct ie_softc *sc, int cmd, volatile void *pcmd, int mask)
1284581Srgrimes{
128526996Sgibbs	volatile struct ie_cmd_common *cc = pcmd;
128626996Sgibbs	volatile int timedout = 0;
128729677Sgibbs	struct	 callout_handle ch;
1288581Srgrimes
1289112734Smdodd	sc->scb->ie_command = (u_short) cmd;
12908876Srgrimes
129126996Sgibbs	if (IE_ACTION_COMMAND(cmd) && pcmd) {
1292112734Smdodd		(*sc->ie_chan_attn) (sc);
1293581Srgrimes
129426996Sgibbs		/*
129526996Sgibbs		 * According to the packet driver, the minimum timeout
129626996Sgibbs		 * should be .369 seconds, which we round up to .37.
129726996Sgibbs		 */
129829677Sgibbs		ch = timeout(chan_attn_timeout, (caddr_t)&timedout,
129929677Sgibbs			     37 * hz / 100);
130026996Sgibbs		/* ignore cast-qual */
1301581Srgrimes
130226996Sgibbs		/*
130326996Sgibbs		 * Now spin-lock waiting for status.  This is not a very
130426996Sgibbs		 * nice thing to do, but I haven't figured out how, or
130526996Sgibbs		 * indeed if, we can put the process waiting for action to
130626996Sgibbs		 * sleep.  (We may be getting called through some other
130726996Sgibbs		 * timeout running in the kernel.)
130826996Sgibbs		 */
130926996Sgibbs		while (1) {
131026996Sgibbs			if ((cc->ie_cmd_status & mask) || timedout)
131126996Sgibbs				break;
131226996Sgibbs		}
1313581Srgrimes
131429677Sgibbs		untimeout(chan_attn_timeout, (caddr_t)&timedout, ch);
131526996Sgibbs		/* ignore cast-qual */
1316581Srgrimes
131726996Sgibbs		return (timedout);
131826996Sgibbs	} else {
13198876Srgrimes
132026996Sgibbs		/*
132126996Sgibbs		 * Otherwise, just wait for the command to be accepted.
132226996Sgibbs		 */
1323112734Smdodd		(*sc->ie_chan_attn) (sc);
1324581Srgrimes
1325112734Smdodd		while (sc->scb->ie_command);	/* spin lock */
1326581Srgrimes
132726996Sgibbs		return (0);
132826996Sgibbs	}
1329581Srgrimes}
1330581Srgrimes
1331581Srgrimes/*
1332581Srgrimes * Run the time-domain reflectometer...
1333581Srgrimes */
133426996Sgibbsstatic void
1335112734Smdoddrun_tdr(struct ie_softc *sc, volatile struct ie_tdr_cmd *cmd)
1336581Srgrimes{
133726996Sgibbs	int	result;
1338581Srgrimes
133926996Sgibbs	cmd->com.ie_cmd_status = 0;
134026996Sgibbs	cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST;
134126996Sgibbs	cmd->com.ie_cmd_link = 0xffff;
134226996Sgibbs	cmd->ie_tdr_time = 0;
1343581Srgrimes
1344112734Smdodd	sc->scb->ie_command_list = MK_16(MEM(sc), cmd);
134526996Sgibbs	cmd->ie_tdr_time = 0;
1346581Srgrimes
1347112734Smdodd	if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL))
134826996Sgibbs		result = 0x2000;
134926996Sgibbs	else
135026996Sgibbs		result = cmd->ie_tdr_time;
1351581Srgrimes
1352112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
1353581Srgrimes
135426996Sgibbs	if (result & IE_TDR_SUCCESS)
135526996Sgibbs		return;
1356581Srgrimes
135726996Sgibbs	if (result & IE_TDR_XCVR) {
1358112734Smdodd		printf("ie%d: transceiver problem\n", sc->unit);
135926996Sgibbs	} else if (result & IE_TDR_OPEN) {
1360112734Smdodd		printf("ie%d: TDR detected an open %d clocks away\n", sc->unit,
136126996Sgibbs		       result & IE_TDR_TIME);
136226996Sgibbs	} else if (result & IE_TDR_SHORT) {
1363112734Smdodd		printf("ie%d: TDR detected a short %d clocks away\n", sc->unit,
136426996Sgibbs		       result & IE_TDR_TIME);
136526996Sgibbs	} else {
1366112734Smdodd		printf("ie%d: TDR returned unknown status %x\n", sc->unit, result);
136726996Sgibbs	}
1368581Srgrimes}
1369581Srgrimes
137026996Sgibbsstatic void
1371112734Smdoddstart_receiver(struct ie_softc *sc)
1372581Srgrimes{
137326996Sgibbs	int	s = splimp();
1374581Srgrimes
1375112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
1376112734Smdodd	command_and_wait(sc, IE_RU_START, 0, 0);
1377581Srgrimes
1378112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
1379581Srgrimes
138026996Sgibbs	splx(s);
1381581Srgrimes}
1382581Srgrimes
1383581Srgrimes/*
1384581Srgrimes * Here is a helper routine for iernr() and ieinit().  This sets up
1385581Srgrimes * the RFA.
1386581Srgrimes */
138743314Sdillonstatic v_caddr_t
1388112734Smdoddsetup_rfa(struct ie_softc *sc, v_caddr_t ptr)
138926996Sgibbs{
139043314Sdillon	volatile struct ie_recv_frame_desc *rfd = (volatile void *)ptr;
139126996Sgibbs	volatile struct ie_recv_buf_desc *rbd;
139226996Sgibbs	int	i;
1393581Srgrimes
139426996Sgibbs	/* First lay them out */
1395112734Smdodd	for (i = 0; i < sc->nframes; i++) {
1396112734Smdodd		sc->rframes[i] = rfd;
139743314Sdillon		bzero((volatile char *) rfd, sizeof *rfd);	/* ignore cast-qual */
139826996Sgibbs		rfd++;
139926996Sgibbs	}
1400581Srgrimes
140147108Sbde	ptr = Alignvol(rfd);		/* ignore cast-qual */
14028876Srgrimes
140326996Sgibbs	/* Now link them together */
1404112734Smdodd	for (i = 0; i < sc->nframes; i++) {
1405112734Smdodd		sc->rframes[i]->ie_fd_next =
1406112734Smdodd		    MK_16(MEM(sc), sc->rframes[(i + 1) % sc->nframes]);
140726996Sgibbs	}
14088876Srgrimes
140926996Sgibbs	/* Finally, set the EOL bit on the last one. */
1410112734Smdodd	sc->rframes[sc->nframes - 1]->ie_fd_last |= IE_FD_LAST;
1411581Srgrimes
141226996Sgibbs	/*
141326996Sgibbs	 * Now lay out some buffers for the incoming frames.  Note that we
141426996Sgibbs	 * set aside a bit of slop in each buffer, to make sure that we have
141526996Sgibbs	 * enough space to hold a single frame in every buffer.
141626996Sgibbs	 */
141743314Sdillon	rbd = (volatile void *) ptr;
1418581Srgrimes
1419112734Smdodd	for (i = 0; i < sc->nrxbufs; i++) {
1420112734Smdodd		sc->rbuffs[i] = rbd;
142143314Sdillon		bzero((volatile char *)rbd, sizeof *rbd);
142247108Sbde		ptr = Alignvol(ptr + sizeof *rbd);
142326996Sgibbs		rbd->ie_rbd_length = IE_RBUF_SIZE;
1424112734Smdodd		rbd->ie_rbd_buffer = MK_24(MEM(sc), ptr);
1425112734Smdodd		sc->cbuffs[i] = (volatile void *) ptr;
142626996Sgibbs		ptr += IE_RBUF_SIZE;
142743314Sdillon		rbd = (volatile void *) ptr;
142826996Sgibbs	}
14298876Srgrimes
143026996Sgibbs	/* Now link them together */
1431112734Smdodd	for (i = 0; i < sc->nrxbufs; i++) {
1432112734Smdodd		sc->rbuffs[i]->ie_rbd_next =
1433112734Smdodd		    MK_16(MEM(sc), sc->rbuffs[(i + 1) % sc->nrxbufs]);
143426996Sgibbs	}
14358876Srgrimes
143626996Sgibbs	/* Tag EOF on the last one */
1437112734Smdodd	sc->rbuffs[sc->nrxbufs - 1]->ie_rbd_length |= IE_RBD_LAST;
1438581Srgrimes
143926996Sgibbs	/*
144026996Sgibbs	 * We use the head and tail pointers on receive to keep track of the
144126996Sgibbs	 * order in which RFDs and RBDs are used.
144226996Sgibbs	 */
1443112734Smdodd	sc->rfhead = 0;
1444112734Smdodd	sc->rftail = sc->nframes - 1;
1445112734Smdodd	sc->rbhead = 0;
1446112734Smdodd	sc->rbtail = sc->nrxbufs - 1;
1447581Srgrimes
1448112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
1449112734Smdodd	sc->rframes[0]->ie_fd_buf_desc = MK_16(MEM(sc), sc->rbuffs[0]);
1450581Srgrimes
145147108Sbde	ptr = Alignvol(ptr);
145226996Sgibbs	return (ptr);
1453581Srgrimes}
1454581Srgrimes
1455581Srgrimes/*
1456581Srgrimes * Run the multicast setup command.
1457581Srgrimes * Call at splimp().
1458581Srgrimes */
145926996Sgibbsstatic int
1460112734Smdoddmc_setup(struct ie_softc *sc)
146126996Sgibbs{
1462112734Smdodd	volatile struct ie_mcast_cmd *cmd = (volatile void *)sc->xmit_cbuffs[0];
14638876Srgrimes
146426996Sgibbs	cmd->com.ie_cmd_status = 0;
146526996Sgibbs	cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST;
146626996Sgibbs	cmd->com.ie_cmd_link = 0xffff;
14678876Srgrimes
146826996Sgibbs	/* ignore cast-qual */
1469112734Smdodd	bcopy((v_caddr_t) sc->mcast_addrs, (v_caddr_t) cmd->ie_mcast_addrs,
1470112734Smdodd	      sc->mcast_count * sizeof *sc->mcast_addrs);
1471581Srgrimes
1472112734Smdodd	cmd->ie_mcast_bytes = sc->mcast_count * 6;	/* grrr... */
14738876Srgrimes
1474112734Smdodd	sc->scb->ie_command_list = MK_16(MEM(sc), cmd);
1475112734Smdodd	if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)
147626996Sgibbs	    || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
1477112734Smdodd		printf("ie%d: multicast address setup command failed\n", sc->unit);
147826996Sgibbs		return (0);
147926996Sgibbs	}
148026996Sgibbs	return (1);
1481581Srgrimes}
1482581Srgrimes
1483581Srgrimes/*
1484581Srgrimes * This routine takes the environment generated by check_ie_present()
1485581Srgrimes * and adds to it all the other structures we need to operate the adapter.
1486581Srgrimes * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands,
1487581Srgrimes * starting the receiver unit, and clearing interrupts.
1488581Srgrimes *
1489581Srgrimes * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER.
1490581Srgrimes */
1491798Swollmanstatic void
149250084Smdoddieinit(xsc)
149350084Smdodd	void *xsc;
1494581Srgrimes{
1495112734Smdodd	struct ie_softc *sc = xsc;
1496112734Smdodd	volatile struct ie_sys_ctl_block *scb = sc->scb;
1497112734Smdodd	caddr_t ptr;
149826996Sgibbs	int	i;
1499112734Smdodd	int	unit = sc->unit;
1500581Srgrimes
150147108Sbde	ptr = Alignvol((volatile char *) scb + sizeof *scb);
1502581Srgrimes
150326996Sgibbs	/*
150426996Sgibbs	 * Send the configure command first.
150526996Sgibbs	 */
150626996Sgibbs	{
150743314Sdillon		volatile struct ie_config_cmd *cmd = (volatile void *) ptr;
1508581Srgrimes
1509112734Smdodd		ie_setup_config(cmd, sc->promisc,
1510112734Smdodd				sc->hard_type == IE_STARLAN10);
151126996Sgibbs		cmd->com.ie_cmd_status = 0;
151226996Sgibbs		cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST;
151326996Sgibbs		cmd->com.ie_cmd_link = 0xffff;
1514581Srgrimes
1515112734Smdodd		scb->ie_command_list = MK_16(MEM(sc), cmd);
1516581Srgrimes
1517112734Smdodd		if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)
151826996Sgibbs		 || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
151950086Smdodd			printf("ie%d: configure command failed\n", unit);
152026996Sgibbs			return;
152126996Sgibbs		}
152226996Sgibbs	}
152326996Sgibbs	/*
152426996Sgibbs	 * Now send the Individual Address Setup command.
152526996Sgibbs	 */
152626996Sgibbs	{
152743314Sdillon		volatile struct ie_iasetup_cmd *cmd = (volatile void *) ptr;
1528581Srgrimes
152926996Sgibbs		cmd->com.ie_cmd_status = 0;
153026996Sgibbs		cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST;
153126996Sgibbs		cmd->com.ie_cmd_link = 0xffff;
1532581Srgrimes
1533147256Sbrooks		bcopy((volatile char *)IFP2ENADDR(sc->ifp),
153443314Sdillon		      (volatile char *)&cmd->ie_address, sizeof cmd->ie_address);
1535112734Smdodd		scb->ie_command_list = MK_16(MEM(sc), cmd);
1536112734Smdodd		if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)
153726996Sgibbs		    || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
153826996Sgibbs			printf("ie%d: individual address "
1539112734Smdodd			       "setup command failed\n", sc->unit);
154026996Sgibbs			return;
154126996Sgibbs		}
154226996Sgibbs	}
1543581Srgrimes
154426996Sgibbs	/*
154526996Sgibbs	 * Now run the time-domain reflectometer.
154626996Sgibbs	 */
1547112734Smdodd	run_tdr(sc, (volatile void *) ptr);
1548581Srgrimes
154926996Sgibbs	/*
155026996Sgibbs	 * Acknowledge any interrupts we have generated thus far.
155126996Sgibbs	 */
1552112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
1553581Srgrimes
155426996Sgibbs	/*
155526996Sgibbs	 * Set up the RFA.
155626996Sgibbs	 */
1557112734Smdodd	ptr = setup_rfa(sc, ptr);
1558581Srgrimes
155926996Sgibbs	/*
156026996Sgibbs	 * Finally, the transmit command and buffer are the last little bit
156126996Sgibbs	 * of work.
156226996Sgibbs	 */
1563581Srgrimes
156426996Sgibbs	/* transmit command buffers */
1565112734Smdodd	for (i = 0; i < sc->ntxbufs; i++) {
1566112734Smdodd		sc->xmit_cmds[i] = (volatile void *) ptr;
1567112734Smdodd		ptr += sizeof *sc->xmit_cmds[i];
156847108Sbde		ptr = Alignvol(ptr);
1569112734Smdodd		sc->xmit_buffs[i] = (volatile void *)ptr;
1570112734Smdodd		ptr += sizeof *sc->xmit_buffs[i];
157147108Sbde		ptr = Alignvol(ptr);
157226996Sgibbs	}
1573581Srgrimes
157426996Sgibbs	/* transmit buffers */
1575112734Smdodd	for (i = 0; i < sc->ntxbufs - 1; i++) {
1576112734Smdodd		sc->xmit_cbuffs[i] = (volatile void *)ptr;
157726996Sgibbs		ptr += IE_BUF_LEN;
157847108Sbde		ptr = Alignvol(ptr);
157926996Sgibbs	}
1580112734Smdodd	sc->xmit_cbuffs[sc->ntxbufs - 1] = (volatile void *) ptr;
1581581Srgrimes
1582112734Smdodd	for (i = 1; i < sc->ntxbufs; i++) {
1583112734Smdodd		bzero((v_caddr_t) sc->xmit_cmds[i], sizeof *sc->xmit_cmds[i]);
1584112734Smdodd		bzero((v_caddr_t) sc->xmit_buffs[i], sizeof *sc->xmit_buffs[i]);
158526996Sgibbs	}
1586581Srgrimes
158726996Sgibbs	/*
158826996Sgibbs	 * This must be coordinated with iestart() and ietint().
158926996Sgibbs	 */
1590112734Smdodd	sc->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL;
1591581Srgrimes
159226996Sgibbs	/* take the ee16 out of loopback */
1593112734Smdodd	if (sc->hard_type == IE_EE16) {
159426996Sgibbs		u_int8_t bart_config;
1595581Srgrimes
1596112734Smdodd		bart_config = inb(PORT(sc) + IEE16_CONFIG);
159726996Sgibbs		bart_config &= ~IEE16_BART_LOOPBACK;
159826996Sgibbs		/* inb doesn't get bit! */
159926996Sgibbs		bart_config |= IEE16_BART_MCS16_TEST;
1600112734Smdodd		outb(PORT(sc) + IEE16_CONFIG, bart_config);
1601112734Smdodd		ee16_interrupt_enable(sc);
1602112734Smdodd		ee16_chan_attn(sc);
160326996Sgibbs	}
1604147256Sbrooks	sc->ifp->if_flags |= IFF_RUNNING;	/* tell higher levels
160526996Sgibbs							 * we're here */
1606147256Sbrooks	sc->ifp->if_flags &= ~IFF_OACTIVE;
1607112788Smdodd
1608112734Smdodd	start_receiver(sc);
160925971Sgibbs
161026996Sgibbs	return;
1611581Srgrimes}
1612581Srgrimes
161326996Sgibbsstatic void
1614112734Smdoddie_stop(struct ie_softc *sc)
1615581Srgrimes{
1616112734Smdodd	command_and_wait(sc, IE_RU_DISABLE, 0, 0);
1617581Srgrimes}
1618581Srgrimes
1619798Swollmanstatic int
162036735Sdfrieioctl(struct ifnet *ifp, u_long command, caddr_t data)
1621581Srgrimes{
162226996Sgibbs	int	s, error = 0;
1623112734Smdodd	struct	 ie_softc *sc = ifp->if_softc;
1624581Srgrimes
162526996Sgibbs	s = splimp();
1626581Srgrimes
162726996Sgibbs	switch (command) {
162826996Sgibbs	case SIOCSIFFLAGS:
162926996Sgibbs		/*
163026996Sgibbs		 * Note that this device doesn't have an "all multicast"
163126996Sgibbs		 * mode, so we must turn on promiscuous mode and do the
163226996Sgibbs		 * filtering manually.
163326996Sgibbs		 */
163426996Sgibbs		if ((ifp->if_flags & IFF_UP) == 0 &&
163526996Sgibbs		    (ifp->if_flags & IFF_RUNNING)) {
163626996Sgibbs			ifp->if_flags &= ~IFF_RUNNING;
1637112734Smdodd			ie_stop(sc);
163826996Sgibbs		} else if ((ifp->if_flags & IFF_UP) &&
163926996Sgibbs			   (ifp->if_flags & IFF_RUNNING) == 0) {
1640112734Smdodd			sc->promisc =
164126996Sgibbs			    ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
1642112734Smdodd			ieinit(sc);
1643112734Smdodd		} else if (sc->promisc ^
164426996Sgibbs			   (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) {
1645112734Smdodd			sc->promisc =
164626996Sgibbs			    ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
1647112734Smdodd			ieinit(sc);
164826996Sgibbs		}
164926996Sgibbs		break;
1650581Srgrimes
165126996Sgibbs	case SIOCADDMULTI:
165226996Sgibbs	case SIOCDELMULTI:
165326996Sgibbs		/*
165426996Sgibbs		 * Update multicast listeners
165526996Sgibbs		 */
165626996Sgibbs		/* reset multicast filtering */
1657112734Smdodd		ie_mc_reset(sc);
165826996Sgibbs		error = 0;
165926996Sgibbs		break;
1660581Srgrimes
166126996Sgibbs	default:
1662106937Ssam		error = ether_ioctl(ifp, command, data);
1663106937Ssam		break;
166426996Sgibbs	}
1665581Srgrimes
166626996Sgibbs	splx(s);
166726996Sgibbs	return (error);
1668581Srgrimes}
1669581Srgrimes
167026996Sgibbsstatic void
1671112734Smdoddie_mc_reset(struct ie_softc *sc)
167226996Sgibbs{
167326996Sgibbs	struct ifmultiaddr *ifma;
1674581Srgrimes
167526996Sgibbs	/*
167626996Sgibbs	 * Step through the list of addresses.
167726996Sgibbs	 */
1678112734Smdodd	sc->mcast_count = 0;
1679147256Sbrooks	TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
168026996Sgibbs		if (ifma->ifma_addr->sa_family != AF_LINK)
168126996Sgibbs			continue;
1682581Srgrimes
168326996Sgibbs		/* XXX - this is broken... */
1684112734Smdodd		if (sc->mcast_count >= MAXMCAST) {
1685147256Sbrooks			sc->ifp->if_flags |= IFF_ALLMULTI;
1686147256Sbrooks			ieioctl(sc->ifp, SIOCSIFFLAGS, (void *) 0);
168726996Sgibbs			goto setflag;
168826996Sgibbs		}
168926996Sgibbs		bcopy(LLADDR((struct sockaddr_dl *) ifma->ifma_addr),
1690112734Smdodd		      &(sc->mcast_addrs[sc->mcast_count]), 6);
1691112734Smdodd		sc->mcast_count++;
169226996Sgibbs	}
169321666Swollman
1694581Srgrimessetflag:
1695112734Smdodd	sc->want_mcsetup = 1;
1696581Srgrimes}
1697581Srgrimes
1698581Srgrimes
1699581Srgrimes#ifdef DEBUG
170033181Seivindstatic void
170126996Sgibbsprint_rbd(volatile struct ie_recv_buf_desc * rbd)
170226996Sgibbs{
170338224Sbde	printf("RBD at %p:\n"
170438224Sbde	       "actual %04x, next %04x, buffer %p\n"
170526996Sgibbs	       "length %04x, mbz %04x\n",
170643314Sdillon	       (volatile void *) rbd,
170737618Sbde	       rbd->ie_rbd_actual, rbd->ie_rbd_next,
170837618Sbde	       (void *) rbd->ie_rbd_buffer,
170926996Sgibbs	       rbd->ie_rbd_length, rbd->mbz);
1710581Srgrimes}
1711581Srgrimes
171226996Sgibbs#endif				/* DEBUG */
1713112790Smdodd
1714112790Smdoddint
1715112790Smdoddie_alloc_resources (device_t dev)
1716112790Smdodd{
1717112790Smdodd	struct ie_softc *       sc;
1718112790Smdodd	int                     error;
1719112790Smdodd
1720112790Smdodd	error = 0;
1721112790Smdodd	sc = device_get_softc(dev);
1722112790Smdodd
1723127135Snjl	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
1724127135Snjl					    RF_ACTIVE);
1725112790Smdodd	if (!sc->io_res) {
1726112790Smdodd		device_printf(dev, "No I/O space?!\n");
1727112790Smdodd		error = ENOMEM;
1728112790Smdodd		goto bad;
1729112790Smdodd	}
1730112790Smdodd	sc->io_bt = rman_get_bustag(sc->io_res);
1731112790Smdodd	sc->io_bh = rman_get_bushandle(sc->io_res);
1732112790Smdodd
1733127135Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
1734127135Snjl					     RF_ACTIVE);
1735112790Smdodd	if (!sc->mem_res) {
1736112790Smdodd                device_printf(dev, "No Memory!\n");
1737112790Smdodd		error = ENOMEM;
1738112790Smdodd		goto bad;
1739112790Smdodd	}
1740112790Smdodd	sc->mem_bt = rman_get_bustag(sc->mem_res);
1741112790Smdodd	sc->mem_bh = rman_get_bushandle(sc->mem_res);
1742112790Smdodd
1743127135Snjl	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
1744127135Snjl					     RF_ACTIVE);
1745112790Smdodd	if (!sc->irq_res) {
1746112790Smdodd		device_printf(dev, "No IRQ!\n");
1747112790Smdodd		error = ENOMEM;
1748112790Smdodd		goto bad;
1749112790Smdodd	}
1750112790Smdodd
1751112790Smdodd	sc->port = rman_get_start(sc->io_res);  /* XXX hack */
1752112790Smdodd	sc->iomembot = rman_get_virtual(sc->mem_res);
1753112790Smdodd	sc->iosize = rman_get_size(sc->mem_res);
1754112790Smdodd
1755112790Smdodd	return (0);
1756112790Smdoddbad:
1757112790Smdodd	return (error);
1758112790Smdodd}
1759112790Smdodd
1760112790Smdoddvoid
1761112790Smdoddie_release_resources (device_t dev)
1762112790Smdodd{
1763112790Smdodd	struct ie_softc *       sc;
1764112790Smdodd
1765112790Smdodd	sc = device_get_softc(dev);
1766112790Smdodd
1767112790Smdodd	if (sc->irq_ih)
1768112790Smdodd		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
1769112790Smdodd	if (sc->io_res)
1770112790Smdodd		bus_release_resource(dev, SYS_RES_IOPORT,
1771112790Smdodd				     sc->io_rid, sc->io_res);
1772112790Smdodd	if (sc->irq_res)
1773112790Smdodd		bus_release_resource(dev, SYS_RES_IRQ,
1774112790Smdodd				     sc->irq_rid, sc->irq_res);
1775112790Smdodd	if (sc->mem_res)
1776112790Smdodd		bus_release_resource(dev, SYS_RES_MEMORY,
1777112790Smdodd				     sc->mem_rid, sc->mem_res);
1778112790Smdodd
1779112790Smdodd	return;
1780112790Smdodd}
1781112790Smdodd
1782112790Smdoddint
1783112790Smdoddie_detach (device_t dev)
1784112790Smdodd{
1785112790Smdodd	struct ie_softc *	sc;
1786112790Smdodd	struct ifnet *		ifp;
1787112790Smdodd
1788112790Smdodd	sc = device_get_softc(dev);
1789147256Sbrooks	ifp = sc->ifp;
1790112790Smdodd
1791112790Smdodd	if (sc->hard_type == IE_EE16)
1792112790Smdodd		ee16_shutdown(sc, 0);
1793112790Smdodd
1794112790Smdodd	ie_stop(sc);
1795112790Smdodd	ifp->if_flags &= ~IFF_RUNNING;
1796112790Smdodd	ether_ifdetach(ifp);
1797147256Sbrooks	if_free(ifp);
1798112790Smdodd	ie_release_resources(dev);
1799112790Smdodd
1800112790Smdodd	return (0);
1801112790Smdodd}
1802