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: releng/11.0/sys/dev/ie/if_ie.c 276750 2015-01-06 12:59:37Z rwatson $");
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>
133257176Sglebius#include <net/if_var.h>
1342056Swollman#include <net/if_types.h>
1352056Swollman#include <net/if_dl.h>
136581Srgrimes
13750026Smdodd#include <netinet/in.h>
13850026Smdodd#include <netinet/if_ether.h>
13950026Smdodd
14079077Simp#include <dev/ic/i82586.h>
141112790Smdodd#include <dev/ie/if_ievar.h>
14255953Speter#include <dev/ie/if_iereg.h>
14355953Speter#include <dev/ie/if_ie507.h>
14455953Speter#include <dev/ie/if_iee16.h>
1452268Sats#include <i386/isa/elink.h>
146581Srgrimes
1472056Swollman#include <net/bpf.h>
148581Srgrimes
149581Srgrimes#ifdef DEBUG
15026996Sgibbs#define IED_RINT	0x01
15126996Sgibbs#define IED_TINT	0x02
15226996Sgibbs#define IED_RNR		0x04
15326996Sgibbs#define IED_CNA		0x08
15426996Sgibbs#define IED_READFRAME	0x10
15533181Seivindstatic int	ie_debug = IED_RNR;
15626996Sgibbs
157581Srgrimes#endif
158581Srgrimes
15926996Sgibbs#define IE_BUF_LEN	ETHER_MAX_LEN	/* length of transmit buffer */
160581Srgrimes
161230808Spluknet/* XXX this driver uses `volatile' and `caddr_t' to a fault. */
162230808Spluknettypedef	volatile char *v_caddr_t;	/* core address, pointer to volatile */
163230808Spluknet
164581Srgrimes/* Forward declaration */
165581Srgrimesstruct ie_softc;
166581Srgrimes
167112734Smdoddstatic void	ieinit			(void *);
168179491Sjhbstatic void	ieinit_locked		(struct ie_softc *);
169112734Smdoddstatic void	ie_stop			(struct ie_softc *);
170112734Smdoddstatic int	ieioctl			(struct ifnet *, u_long, caddr_t);
171112734Smdoddstatic void	iestart			(struct ifnet *);
172179491Sjhbstatic void	iestart_locked		(struct ifnet *);
17324909Sgibbs
174112734Smdoddstatic __inline void
175112734Smdodd		ee16_interrupt_enable	(struct ie_softc *);
17624909Sgibbs
177112734Smdoddstatic __inline void
178112734Smdodd		ie_ack			(struct ie_softc *, u_int);
179112734Smdoddstatic void	iereset			(struct ie_softc *);
180112734Smdoddstatic void	ie_readframe		(struct ie_softc *, int);
181112734Smdoddstatic void	ie_drop_packet_buffer	(struct ie_softc *);
182112734Smdoddstatic int	command_and_wait	(struct ie_softc *,
183112734Smdodd					 int, void volatile *, int);
184112734Smdoddstatic void	run_tdr			(struct ie_softc *,
185112734Smdodd					 volatile struct ie_tdr_cmd *);
186112734Smdoddstatic int	ierint			(struct ie_softc *);
187112734Smdoddstatic int	ietint			(struct ie_softc *);
188112734Smdoddstatic int	iernr			(struct ie_softc *);
189112734Smdoddstatic void	start_receiver		(struct ie_softc *);
190112734Smdoddstatic __inline int
191112734Smdodd		ieget			(struct ie_softc *, struct mbuf **);
192112734Smdoddstatic v_caddr_t setup_rfa		(struct ie_softc *, v_caddr_t);
193112734Smdoddstatic int	mc_setup		(struct ie_softc *);
194112734Smdoddstatic void	ie_mc_reset		(struct ie_softc *);
195581Srgrimes
196581Srgrimes#ifdef DEBUG
197112734Smdoddstatic void	print_rbd		(volatile struct ie_recv_buf_desc * rbd);
19833181Seivindstatic int	in_ierint = 0;
19933181Seivindstatic int	in_ietint = 0;
200581Srgrimes#endif
201581Srgrimes
20212724Sphkstatic const char *ie_hardware_names[] = {
203112790Smdodd	"None",
20426996Sgibbs	"StarLAN 10",
20526996Sgibbs	"EN100",
20626996Sgibbs	"StarLAN Fiber",
20726996Sgibbs	"3C507",
20826996Sgibbs	"NI5210",
20926996Sgibbs	"EtherExpress 16",
21026996Sgibbs	"Unknown"
211581Srgrimes};
212581Srgrimes
2138876Srgrimes/*
214112765Smdodd * sizeof(iscp) == 1+1+2+4 == 8
215112765Smdodd * sizeof(scb) == 2+2+2+2+2+2+2+2 == 16
216112765Smdodd * NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384
217112765Smdodd * sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18
218112765Smdodd * sizeof(transmit buffer) == 1512
219112765Smdodd * sizeof(transmit buffer desc) == 8
220112765Smdodd * -----
221112765Smdodd * 1946
222112765Smdodd *
223112765Smdodd * NRXBUFS * sizeof(rbd) == NRXBUFS*(2+2+4+2+2) == NRXBUFS*12
224112765Smdodd * NRXBUFS * IE_RBUF_SIZE == NRXBUFS*256
225112765Smdodd *
226112765Smdodd * NRXBUFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53
227112765Smdodd *
228112765Smdodd * With NRXBUFS == 48, this leaves us 1574 bytes for another command or
229112765Smdodd * more buffers.  Another transmit command would be 18+8+1512 == 1538
230112765Smdodd * ---just barely fits!
231112765Smdodd *
232112765Smdodd * Obviously all these would have to be reduced for smaller memory sizes.
233112765Smdodd * With a larger memory, it would be possible to roughly double the number
234112765Smdodd * of both transmit and receive buffers.
235112765Smdodd */
236581Srgrimes
237112783Smdodd#define	NFRAMES		4	/* number of receive frames */
238112783Smdodd#define	NRXBUFS		24	/* number of buffers to allocate */
239112783Smdodd#define	IE_RBUF_SIZE	256	/* size of each buffer, MUST BE POWER OF TWO */
240112783Smdodd#define	NTXBUFS		1	/* number of transmit commands */
241112783Smdodd#define	IE_TBUF_SIZE	ETHER_MAX_LEN	/* size of transmit buffer */
242581Srgrimes
24338232Sbde#define MK_24(base, ptr) ((caddr_t)((uintptr_t)ptr - (uintptr_t)base))
24438232Sbde#define MK_16(base, ptr) ((u_short)(uintptr_t)MK_24(base, ptr))
245581Srgrimes
246181134Sjhbvoid
247181134Sjhbee16_shutdown(struct ie_softc *sc)
24824909Sgibbs{
24924909Sgibbs
250112734Smdodd	ee16_reset_586(sc);
251112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_ASIC);
252112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, 0);
25324909Sgibbs}
25424909Sgibbs
255581Srgrimes/*
256581Srgrimes * Taken almost exactly from Bill's if_is.c, then modified beyond recognition.
257581Srgrimes */
258112790Smdoddint
259112790Smdoddie_attach(device_t dev)
260581Srgrimes{
261112790Smdodd	struct ie_softc *       sc;
262112790Smdodd	struct ifnet *          ifp;
263112790Smdodd	size_t                  allocsize;
264179491Sjhb	int                     error, factor;
265581Srgrimes
266112790Smdodd	sc = device_get_softc(dev);
267147256Sbrooks	ifp = sc->ifp = if_alloc(IFT_ETHER);
268147256Sbrooks	if (ifp == NULL) {
269147256Sbrooks		device_printf(sc->dev, "can not if_alloc()\n");
270147256Sbrooks		return (ENOSPC);
271147256Sbrooks	}
27240565Sbde
273112790Smdodd	sc->dev = dev;
274179491Sjhb	mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
275179491Sjhb	    MTX_DEF);
276112790Smdodd
27726996Sgibbs	/*
27826996Sgibbs	 * based on the amount of memory we have, allocate our tx and rx
27926996Sgibbs	 * resources.
28026996Sgibbs	 */
281112790Smdodd	factor = rman_get_size(sc->mem_res) / 8192;
282112734Smdodd	sc->nframes = factor * NFRAMES;
283112734Smdodd	sc->nrxbufs = factor * NRXBUFS;
284112734Smdodd	sc->ntxbufs = factor * NTXBUFS;
28525971Sgibbs
28626996Sgibbs	/*
28726996Sgibbs	 * Since all of these guys are arrays of pointers, allocate as one
28826996Sgibbs	 * big chunk and dole out accordingly.
28926996Sgibbs	 */
290112734Smdodd	allocsize = sizeof(void *) * (sc->nframes
291112734Smdodd				      + (sc->nrxbufs * 2)
292112734Smdodd				      + (sc->ntxbufs * 3));
293112734Smdodd	sc->rframes = (volatile struct ie_recv_frame_desc **) malloc(allocsize,
29426996Sgibbs								     M_DEVBUF,
29526996Sgibbs								   M_NOWAIT);
296147256Sbrooks	if (sc->rframes == NULL) {
297179491Sjhb		mtx_destroy(&sc->lock);
298112790Smdodd		return (ENXIO);
299147256Sbrooks	}
300112734Smdodd	sc->rbuffs =
301112734Smdodd	    (volatile struct ie_recv_buf_desc **)&sc->rframes[sc->nframes];
302112734Smdodd	sc->cbuffs = (volatile u_char **)&sc->rbuffs[sc->nrxbufs];
303112734Smdodd	sc->xmit_cmds =
304112734Smdodd	    (volatile struct ie_xmit_cmd **)&sc->cbuffs[sc->nrxbufs];
305112734Smdodd	sc->xmit_buffs =
306112734Smdodd	    (volatile struct ie_xmit_buf **)&sc->xmit_cmds[sc->ntxbufs];
307112734Smdodd	sc->xmit_cbuffs = (volatile u_char **)&sc->xmit_buffs[sc->ntxbufs];
30825971Sgibbs
309112790Smdodd	if (bootverbose)
310112790Smdodd		device_printf(sc->dev, "hardware type %s, revision %d\n",
311112790Smdodd			ie_hardware_names[sc->hard_type], sc->hard_vers + 1);
312112790Smdodd
313112734Smdodd	ifp->if_softc = sc;
314121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
315179491Sjhb	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
31626996Sgibbs	ifp->if_start = iestart;
31726996Sgibbs	ifp->if_ioctl = ieioctl;
31850084Smdodd	ifp->if_init = ieinit;
319207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
3208876Srgrimes
321147256Sbrooks	ether_ifattach(ifp, sc->enaddr);
322179491Sjhb
323179491Sjhb	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
324179491Sjhb				NULL, ie_intr, sc, &sc->irq_ih);
325179491Sjhb	if (error) {
326179491Sjhb		device_printf(dev, "Unable to register interrupt handler\n");
327179491Sjhb		mtx_destroy(&sc->lock);
328179491Sjhb		return (error);
329179491Sjhb	}
330179491Sjhb
331112790Smdodd	return (0);
332581Srgrimes}
333581Srgrimes
334117877Sphkstatic __inline void
335117877Sphkie_ack(struct ie_softc *sc, u_int mask)
336117877Sphk{
337117877Sphk
338117877Sphk	sc->scb->ie_command = sc->scb->ie_status & mask;
339117877Sphk	(*sc->ie_chan_attn) (sc);
340117877Sphk}
341117877Sphk
342581Srgrimes/*
343581Srgrimes * What to do upon receipt of an interrupt.
344581Srgrimes */
345112790Smdoddvoid
346112790Smdoddie_intr(void *xsc)
347581Srgrimes{
348112790Smdodd	struct ie_softc *sc = (struct ie_softc *)xsc;
349112734Smdodd	u_short status;
350581Srgrimes
351179491Sjhb	IE_LOCK(sc);
352179491Sjhb
35326996Sgibbs	/* Clear the interrupt latch on the 3C507. */
354112734Smdodd	if (sc->hard_type == IE_3C507
355112734Smdodd	 && (inb(PORT(sc) + IE507_CTRL) & EL_CTRL_INTL))
356112734Smdodd		outb(PORT(sc) + IE507_ICTRL, 1);
35724909Sgibbs
35826996Sgibbs	/* disable interrupts on the EE16. */
359112734Smdodd	if (sc->hard_type == IE_EE16)
360112734Smdodd		outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded);
36124909Sgibbs
362112734Smdodd	status = sc->scb->ie_status;
363581Srgrimes
364581Srgrimesloop:
36525971Sgibbs
36626996Sgibbs	/* Don't ack interrupts which we didn't receive */
367112734Smdodd	ie_ack(sc, IE_ST_WHENCE & status);
36825971Sgibbs
36926996Sgibbs	if (status & (IE_ST_RECV | IE_ST_RNR)) {
370581Srgrimes#ifdef DEBUG
37126996Sgibbs		in_ierint++;
37226996Sgibbs		if (ie_debug & IED_RINT)
373179435Sjhb			if_printf(sc->ifp, "rint\n");
374581Srgrimes#endif
375112734Smdodd		ierint(sc);
376581Srgrimes#ifdef DEBUG
37726996Sgibbs		in_ierint--;
378581Srgrimes#endif
37926996Sgibbs	}
38026996Sgibbs	if (status & IE_ST_DONE) {
381581Srgrimes#ifdef DEBUG
38226996Sgibbs		in_ietint++;
38326996Sgibbs		if (ie_debug & IED_TINT)
384179435Sjhb			if_printf(sc->ifp, "tint\n");
385581Srgrimes#endif
386112734Smdodd		ietint(sc);
387581Srgrimes#ifdef DEBUG
38826996Sgibbs		in_ietint--;
389581Srgrimes#endif
39026996Sgibbs	}
39126996Sgibbs	if (status & IE_ST_RNR) {
392581Srgrimes#ifdef DEBUG
39326996Sgibbs		if (ie_debug & IED_RNR)
394179435Sjhb			if_printf(sc->ifp, "rnr\n");
395581Srgrimes#endif
396112734Smdodd		iernr(sc);
39726996Sgibbs	}
398581Srgrimes#ifdef DEBUG
399112734Smdodd	if ((status & IE_ST_ALLDONE) && (ie_debug & IED_CNA))
400179435Sjhb		if_printf(sc->ifp, "cna\n");
401581Srgrimes#endif
402581Srgrimes
403112734Smdodd	if ((status = sc->scb->ie_status) & IE_ST_WHENCE)
40426996Sgibbs		goto loop;
405581Srgrimes
40626996Sgibbs	/* Clear the interrupt latch on the 3C507. */
407112734Smdodd	if (sc->hard_type == IE_3C507)
408112734Smdodd		outb(PORT(sc) + IE507_ICTRL, 1);
40924909Sgibbs
41026996Sgibbs	/* enable interrupts on the EE16. */
411112734Smdodd	if (sc->hard_type == IE_EE16)
412112734Smdodd		outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE);
413179491Sjhb	IE_UNLOCK(sc);
414581Srgrimes}
415581Srgrimes
416581Srgrimes/*
417581Srgrimes * Process a received-frame interrupt.
418581Srgrimes */
41924909Sgibbsstatic int
420112734Smdoddierint(struct ie_softc *sc)
421581Srgrimes{
42226996Sgibbs	int	i, status;
42326996Sgibbs	static int timesthru = 1024;
424581Srgrimes
425112734Smdodd	i = sc->rfhead;
42626996Sgibbs	while (1) {
427112734Smdodd		status = sc->rframes[i]->ie_fd_status;
428581Srgrimes
42926996Sgibbs		if ((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) {
430271820Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1);
43126996Sgibbs			if (!--timesthru) {
432271820Sglebius				if_inc_counter(sc->ifp, IFCOUNTER_IERRORS,
433112734Smdodd				    sc->scb->ie_err_crc +
434112734Smdodd				    sc->scb->ie_err_align +
435112734Smdodd				    sc->scb->ie_err_resource +
436271820Sglebius				    sc->scb->ie_err_overrun);
437112734Smdodd				sc->scb->ie_err_crc = 0;
438112734Smdodd				sc->scb->ie_err_align = 0;
439112734Smdodd				sc->scb->ie_err_resource = 0;
440112734Smdodd				sc->scb->ie_err_overrun = 0;
44126996Sgibbs				timesthru = 1024;
44226996Sgibbs			}
443112734Smdodd			ie_readframe(sc, i);
44426996Sgibbs		} else {
44526996Sgibbs			if (status & IE_FD_RNR) {
446112734Smdodd				if (!(sc->scb->ie_status & IE_RU_READY)) {
447112734Smdodd					sc->rframes[0]->ie_fd_next =
448112734Smdodd					    MK_16(MEM(sc), sc->rbuffs[0]);
449112734Smdodd					sc->scb->ie_recv_list =
450112734Smdodd					    MK_16(MEM(sc), sc->rframes[0]);
451112734Smdodd					command_and_wait(sc, IE_RU_START, 0, 0);
45226996Sgibbs				}
45326996Sgibbs			}
45426996Sgibbs			break;
45526996Sgibbs		}
456112734Smdodd		i = (i + 1) % sc->nframes;
457581Srgrimes	}
45826996Sgibbs	return (0);
459581Srgrimes}
460581Srgrimes
461581Srgrimes/*
462581Srgrimes * Process a command-complete interrupt.  These are only generated by
46326996Sgibbs * the transmission of frames.	This routine is deceptively simple, since
464581Srgrimes * most of the real work is done by iestart().
465581Srgrimes */
46626996Sgibbsstatic int
467112734Smdoddietint(struct ie_softc *sc)
468581Srgrimes{
469179416Sjhb	struct ifnet *ifp = sc->ifp;
47026996Sgibbs	int	status;
47126996Sgibbs	int	i;
472581Srgrimes
473179416Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
474581Srgrimes
475112734Smdodd	for (i = 0; i < sc->xmit_count; i++) {
476112734Smdodd		status = sc->xmit_cmds[i]->ie_xmit_status;
477581Srgrimes
47826996Sgibbs		if (status & IE_XS_LATECOLL) {
479179416Sjhb			if_printf(ifp, "late collision\n");
480271820Sglebius			if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
481271820Sglebius			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
48226996Sgibbs		} else if (status & IE_XS_NOCARRIER) {
483179416Sjhb			if_printf(ifp, "no carrier\n");
484271820Sglebius			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
48526996Sgibbs		} else if (status & IE_XS_LOSTCTS) {
486179416Sjhb			if_printf(ifp, "lost CTS\n");
487271820Sglebius			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
48826996Sgibbs		} else if (status & IE_XS_UNDERRUN) {
489179416Sjhb			if_printf(ifp, "DMA underrun\n");
490271820Sglebius			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
49126996Sgibbs		} else if (status & IE_XS_EXCMAX) {
492179416Sjhb			if_printf(ifp, "too many collisions\n");
493271820Sglebius			if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 16);
494271820Sglebius			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
49526996Sgibbs		} else {
496271820Sglebius			if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
497271820Sglebius			if_inc_counter(ifp, IFCOUNTER_COLLISIONS, status & IE_XS_MAXCOLL);
49826996Sgibbs		}
49926996Sgibbs	}
500112734Smdodd	sc->xmit_count = 0;
501581Srgrimes
50226996Sgibbs	/*
50326996Sgibbs	 * If multicast addresses were added or deleted while we were
50426996Sgibbs	 * transmitting, ie_mc_reset() set the want_mcsetup flag indicating
50526996Sgibbs	 * that we should do it.
50626996Sgibbs	 */
507112734Smdodd	if (sc->want_mcsetup) {
508112734Smdodd		mc_setup(sc);
509112734Smdodd		sc->want_mcsetup = 0;
51026996Sgibbs	}
51126996Sgibbs	/* Wish I knew why this seems to be necessary... */
512112734Smdodd	sc->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL;
513581Srgrimes
514179491Sjhb	iestart_locked(ifp);
51526996Sgibbs	return (0);		/* shouldn't be necessary */
516581Srgrimes}
517581Srgrimes
518581Srgrimes/*
519581Srgrimes * Process a receiver-not-ready interrupt.  I believe that we get these
520581Srgrimes * when there aren't enough buffers to go around.  For now (FIXME), we
521581Srgrimes * just restart the receiver, and hope everything's ok.
522581Srgrimes */
52326996Sgibbsstatic int
524112734Smdoddiernr(struct ie_softc *sc)
525581Srgrimes{
526581Srgrimes#ifdef doesnt_work
527112734Smdodd	setup_rfa(sc, (v_caddr_t) sc->rframes[0]);
528581Srgrimes
529112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
530112734Smdodd	command_and_wait(sc, IE_RU_START, 0, 0);
531581Srgrimes#else
53226996Sgibbs	/* This doesn't work either, but it doesn't hang either. */
533112734Smdodd	command_and_wait(sc, IE_RU_DISABLE, 0, 0);	/* just in case */
534112734Smdodd	setup_rfa(sc, (v_caddr_t) sc->rframes[0]);	/* ignore cast-qual */
535581Srgrimes
536112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
537112734Smdodd	command_and_wait(sc, IE_RU_START, 0, 0);	/* was ENABLE */
538581Srgrimes
539581Srgrimes#endif
540112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
541581Srgrimes
542271820Sglebius	if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
54326996Sgibbs	return (0);
544581Srgrimes}
545581Srgrimes
546581Srgrimes/*
547581Srgrimes * Compare two Ether/802 addresses for equality, inlined and
54826996Sgibbs * unrolled for speed.	I'd love to have an inline assembler
549581Srgrimes * version of this...
550581Srgrimes */
55135210Sbdestatic __inline int
55226996Sgibbsether_equal(u_char * one, u_char * two)
55326996Sgibbs{
55426996Sgibbs	if (one[0] != two[0])
55526996Sgibbs		return (0);
55626996Sgibbs	if (one[1] != two[1])
55726996Sgibbs		return (0);
55826996Sgibbs	if (one[2] != two[2])
55926996Sgibbs		return (0);
56026996Sgibbs	if (one[3] != two[3])
56126996Sgibbs		return (0);
56226996Sgibbs	if (one[4] != two[4])
56326996Sgibbs		return (0);
56426996Sgibbs	if (one[5] != two[5])
56526996Sgibbs		return (0);
56626996Sgibbs	return 1;
567581Srgrimes}
568581Srgrimes
569581Srgrimes/*
57060536Sarchie * Determine quickly whether we should bother reading in this packet.
57160536Sarchie * This depends on whether BPF and/or bridging is enabled, whether we
57260536Sarchie * are receiving multicast address, and whether promiscuous mode is enabled.
57360536Sarchie * We assume that if IFF_PROMISC is set, then *somebody* wants to see
57460536Sarchie * all incoming packets.
575581Srgrimes */
57635210Sbdestatic __inline int
577112734Smdoddcheck_eh(struct ie_softc *sc, struct ether_header *eh)
57826996Sgibbs{
57960536Sarchie	/* Optimize the common case: normal operation. We've received
58060536Sarchie	   either a unicast with our dest or a multicast packet. */
581112734Smdodd	if (sc->promisc == 0) {
58260536Sarchie		int i;
583581Srgrimes
58460536Sarchie		/* If not multicast, it's definitely for us */
58560536Sarchie		if ((eh->ether_dhost[0] & 1) == 0)
58626996Sgibbs			return (1);
587581Srgrimes
58860536Sarchie		/* Accept broadcasts (loose but fast check) */
58960536Sarchie		if (eh->ether_dhost[0] == 0xff)
59026996Sgibbs			return (1);
591581Srgrimes
59260536Sarchie		/* Compare against our multicast addresses */
593112734Smdodd		for (i = 0; i < sc->mcast_count; i++) {
59426996Sgibbs			if (ether_equal(eh->ether_dhost,
595112734Smdodd			    (u_char *)&sc->mcast_addrs[i]))
59626996Sgibbs				return (1);
59726996Sgibbs		}
59860536Sarchie		return (0);
59960536Sarchie	}
60060536Sarchie
60160536Sarchie	/* Always accept packets when in promiscuous mode */
602112734Smdodd	if ((sc->promisc & IFF_PROMISC) != 0)
60326996Sgibbs		return (1);
604581Srgrimes
60560536Sarchie	/* Always accept packets directed at us */
606152315Sru	if (ether_equal(eh->ether_dhost, IF_LLADDR(sc->ifp)))
60726996Sgibbs		return (1);
608581Srgrimes
60960536Sarchie	/* Must have IFF_ALLMULTI but not IFF_PROMISC set. The chip is
61060536Sarchie	   actually in promiscuous mode, so discard unicast packets. */
61160536Sarchie	return((eh->ether_dhost[0] & 1) != 0);
612581Srgrimes}
613581Srgrimes
614581Srgrimes/*
615581Srgrimes * We want to isolate the bits that have meaning...  This assumes that
616581Srgrimes * IE_RBUF_SIZE is an even power of two.  If somehow the act_len exceeds
617581Srgrimes * the size of the buffer, then we are screwed anyway.
618581Srgrimes */
61935210Sbdestatic __inline int
620112734Smdoddie_buflen(struct ie_softc *sc, int head)
62126996Sgibbs{
622112734Smdodd	return (sc->rbuffs[head]->ie_rbd_actual
62326996Sgibbs		& (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1)));
624581Srgrimes}
625581Srgrimes
62635210Sbdestatic __inline int
627112734Smdoddie_packet_len(struct ie_softc *sc)
62826996Sgibbs{
62926996Sgibbs	int	i;
630112734Smdodd	int	head = sc->rbhead;
63126996Sgibbs	int	acc = 0;
632581Srgrimes
63326996Sgibbs	do {
634112734Smdodd		if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
635581Srgrimes#ifdef DEBUG
636112734Smdodd			print_rbd(sc->rbuffs[sc->rbhead]);
637581Srgrimes#endif
63826996Sgibbs			log(LOG_ERR,
639179416Sjhb			    "%s: receive descriptors out of sync at %d\n",
640179416Sjhb			    sc->ifp->if_xname, sc->rbhead);
641112734Smdodd			iereset(sc);
64226996Sgibbs			return (-1);
64326996Sgibbs		}
644112734Smdodd		i = sc->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST;
645581Srgrimes
646112734Smdodd		acc += ie_buflen(sc, head);
647112734Smdodd		head = (head + 1) % sc->nrxbufs;
64826996Sgibbs	} while (!i);
649581Srgrimes
65026996Sgibbs	return (acc);
651581Srgrimes}
652581Srgrimes
653581Srgrimes/*
654581Srgrimes * Read data off the interface, and turn it into an mbuf chain.
655581Srgrimes *
656581Srgrimes * This code is DRAMATICALLY different from the previous version; this
657581Srgrimes * version tries to allocate the entire mbuf chain up front, given the
658581Srgrimes * length of the data available.  This enables us to allocate mbuf
659581Srgrimes * clusters in many situations where before we would have had a long
660581Srgrimes * chain of partially-full mbufs.  This should help to speed up the
661581Srgrimes * operation considerably.  (Provided that it works, of course.)
662581Srgrimes */
66335210Sbdestatic __inline int
664112734Smdoddieget(struct ie_softc *sc, struct mbuf **mp)
665581Srgrimes{
666112720Smdodd	struct	ether_header eh;
66726996Sgibbs	struct	mbuf *m, *top, **mymp;
66826996Sgibbs	int	offset;
66926996Sgibbs	int	totlen, resid;
67026996Sgibbs	int	thismboff;
67126996Sgibbs	int	head;
672581Srgrimes
673112734Smdodd	totlen = ie_packet_len(sc);
67426996Sgibbs	if (totlen <= 0)
67526996Sgibbs		return (-1);
676581Srgrimes
67726996Sgibbs	/*
67826996Sgibbs	 * Snarf the Ethernet header.
67926996Sgibbs	 */
680179416Sjhb	bcopy(sc->cbuffs[sc->rbhead], &eh, sizeof(struct ether_header));
68126996Sgibbs	/* ignore cast-qual warning here */
682581Srgrimes
68326996Sgibbs	/*
68426996Sgibbs	 * As quickly as possible, check if this packet is for us. If not,
68526996Sgibbs	 * don't waste a single cycle copying the rest of the packet in.
68626996Sgibbs	 * This is only a consideration when FILTER is defined; i.e., when
68726996Sgibbs	 * we are either running BPF or doing multicasting.
68826996Sgibbs	 */
689112734Smdodd	if (!check_eh(sc, &eh)) {
690112734Smdodd		ie_drop_packet_buffer(sc);
69126996Sgibbs		return (-1);
69226996Sgibbs	}
693581Srgrimes
694243857Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
695112720Smdodd	if (!m) {
696112734Smdodd		ie_drop_packet_buffer(sc);
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) {
722243857Sglebius			MGET(m, M_NOWAIT, 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) {
731276750Srwatson			if (MCLGET(m, M_NOWAIT))
73226996Sgibbs				m->m_len = min(resid, MCLBYTES);
73326996Sgibbs		} else {
73426996Sgibbs			if (resid < m->m_len) {
73526996Sgibbs				if (!top && resid + max_linkhdr <= m->m_len)
73626996Sgibbs					m->m_data += max_linkhdr;
73726996Sgibbs				m->m_len = resid;
73826996Sgibbs			}
73926996Sgibbs		}
74026996Sgibbs		resid -= m->m_len;
74126996Sgibbs		*mymp = m;
74226996Sgibbs		mymp = &m->m_next;
74326996Sgibbs	} while (resid > 0);
744581Srgrimes
745112720Smdodd	resid = totlen;					/* remaining data */
746112720Smdodd	offset = 0;					/* packet offset */
747112720Smdodd	thismboff = 0;					/* offset in m */
748112720Smdodd
749106937Ssam	m = top;					/* current mbuf */
750112734Smdodd	head = sc->rbhead;				/* current rx buffer */
751581Srgrimes
75226996Sgibbs	/*
75326996Sgibbs	 * Now we take the mbuf chain (hopefully only one mbuf most of the
75426996Sgibbs	 * time) and stuff the data into it.  There are no possible failures
75526996Sgibbs	 * at or after this point.
75626996Sgibbs	 */
75726996Sgibbs	while (resid > 0) {	/* while there's stuff left */
758112734Smdodd		int	thislen = ie_buflen(sc, head) - offset;
759581Srgrimes
76026996Sgibbs		/*
76126996Sgibbs		 * If too much data for the current mbuf, then fill the
76226996Sgibbs		 * current one up, go to the next one, and try again.
76326996Sgibbs		 */
76426996Sgibbs		if (thislen > m->m_len - thismboff) {
76526996Sgibbs			int	newlen = m->m_len - thismboff;
766581Srgrimes
767112734Smdodd			bcopy((v_caddr_t) (sc->cbuffs[head] + offset),
768112734Smdodd			      mtod(m, caddr_t) +thismboff, (unsigned) newlen);
76926996Sgibbs			/* ignore cast-qual warning */
77026996Sgibbs			m = m->m_next;
771106937Ssam			thismboff = 0;		/* new mbuf, so no offset */
77226996Sgibbs			offset += newlen;	/* we are now this far into
77326996Sgibbs						 * the packet */
77426996Sgibbs			resid -= newlen;	/* so there is this much left
77526996Sgibbs						 * to get */
77626996Sgibbs			continue;
77726996Sgibbs		}
77826996Sgibbs		/*
77926996Sgibbs		 * If there is more than enough space in the mbuf to hold
78026996Sgibbs		 * the contents of this buffer, copy everything in, advance
78126996Sgibbs		 * pointers, and so on.
78226996Sgibbs		 */
78326996Sgibbs		if (thislen < m->m_len - thismboff) {
784112734Smdodd			bcopy((v_caddr_t) (sc->cbuffs[head] + offset),
78526996Sgibbs			    mtod(m, caddr_t) +thismboff, (unsigned) thislen);
78626996Sgibbs			thismboff += thislen;	/* we are this far into the
78726996Sgibbs						 * mbuf */
78826996Sgibbs			resid -= thislen;	/* and this much is left */
78926996Sgibbs			goto nextbuf;
79026996Sgibbs		}
79126996Sgibbs		/*
79226996Sgibbs		 * Otherwise, there is exactly enough space to put this
79326996Sgibbs		 * buffer's contents into the current mbuf.  Do the
79426996Sgibbs		 * combination of the above actions.
79526996Sgibbs		 */
796112734Smdodd		bcopy((v_caddr_t) (sc->cbuffs[head] + offset),
79726996Sgibbs		      mtod(m, caddr_t) + thismboff, (unsigned) thislen);
79826996Sgibbs		m = m->m_next;
79926996Sgibbs		thismboff = 0;		/* new mbuf, start at the beginning */
80026996Sgibbs		resid -= thislen;	/* and we are this far through */
801581Srgrimes
80226996Sgibbs		/*
80326996Sgibbs		 * Advance all the pointers.  We can get here from either of
80426996Sgibbs		 * the last two cases, but never the first.
80526996Sgibbs		 */
806581Srgrimesnextbuf:
80726996Sgibbs		offset = 0;
808112734Smdodd		sc->rbuffs[head]->ie_rbd_actual = 0;
809112734Smdodd		sc->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST;
810112734Smdodd		sc->rbhead = head = (head + 1) % sc->nrxbufs;
811112734Smdodd		sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
812112734Smdodd		sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs;
81326996Sgibbs	}
814581Srgrimes
81526996Sgibbs	/*
81626996Sgibbs	 * Unless something changed strangely while we were doing the copy,
81726996Sgibbs	 * we have now copied everything in from the shared memory. This
81826996Sgibbs	 * means that we are done.
81926996Sgibbs	 */
82026996Sgibbs	return (0);
821581Srgrimes}
822581Srgrimes
823581Srgrimes/*
824581Srgrimes * Read frame NUM from unit UNIT (pre-cached as IE).
825581Srgrimes *
826581Srgrimes * This routine reads the RFD at NUM, and copies in the buffers from
827581Srgrimes * the list of RBD, then rotates the RBD and RFD lists so that the receiver
828581Srgrimes * doesn't start complaining.  Trailers are DROPPED---there's no point
82926996Sgibbs * in wasting time on confusing code to deal with them.	 Hopefully,
830581Srgrimes * this machine will never ARP for trailers anyway.
831581Srgrimes */
83226996Sgibbsstatic void
833112734Smdoddie_readframe(struct ie_softc *sc, int	num/* frame number to read */)
834581Srgrimes{
835147256Sbrooks	struct ifnet *ifp = sc->ifp;
83626996Sgibbs	struct ie_recv_frame_desc rfd;
83726996Sgibbs	struct mbuf *m = 0;
838106937Ssam#ifdef DEBUG
839106937Ssam	struct ether_header *eh;
840106937Ssam#endif
84126996Sgibbs
842112734Smdodd	bcopy((v_caddr_t) (sc->rframes[num]), &rfd,
84326996Sgibbs	      sizeof(struct ie_recv_frame_desc));
844581Srgrimes
84526996Sgibbs	/*
84626996Sgibbs	 * Immediately advance the RFD list, since we we have copied ours
84726996Sgibbs	 * now.
84826996Sgibbs	 */
849112734Smdodd	sc->rframes[num]->ie_fd_status = 0;
850112734Smdodd	sc->rframes[num]->ie_fd_last |= IE_FD_LAST;
851112734Smdodd	sc->rframes[sc->rftail]->ie_fd_last &= ~IE_FD_LAST;
852112734Smdodd	sc->rftail = (sc->rftail + 1) % sc->nframes;
853112734Smdodd	sc->rfhead = (sc->rfhead + 1) % sc->nframes;
854581Srgrimes
85526996Sgibbs	if (rfd.ie_fd_status & IE_FD_OK) {
856112734Smdodd		if (ieget(sc, &m)) {
857271820Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);	/* this counts as an
85826996Sgibbs							 * error */
85926996Sgibbs			return;
86026996Sgibbs		}
86126996Sgibbs	}
862581Srgrimes#ifdef DEBUG
863106937Ssam	eh = mtod(m, struct ether_header *);
86426996Sgibbs	if (ie_debug & IED_READFRAME) {
865179416Sjhb		if_printf(ifp, "frame from ether %6D type %x\n",
866106937Ssam		       eh->ether_shost, ":", (unsigned) eh->ether_type);
86726996Sgibbs	}
868106937Ssam	if (ntohs(eh->ether_type) > ETHERTYPE_TRAIL
869106937Ssam	    && ntohs(eh->ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER))
87026996Sgibbs		printf("received trailer!\n");
871581Srgrimes#endif
872581Srgrimes
87326996Sgibbs	if (!m)
87426996Sgibbs		return;
875581Srgrimes
87626996Sgibbs	/*
87726996Sgibbs	 * Finally pass this packet up to higher layers.
87826996Sgibbs	 */
879179491Sjhb	IE_UNLOCK(sc);
880106937Ssam	(*ifp->if_input)(ifp, m);
881179491Sjhb	IE_LOCK(sc);
882581Srgrimes}
883581Srgrimes
88426996Sgibbsstatic void
885112734Smdoddie_drop_packet_buffer(struct ie_softc *sc)
88626996Sgibbs{
88726996Sgibbs	int	i;
888581Srgrimes
88926996Sgibbs	do {
89026996Sgibbs		/*
89126996Sgibbs		 * This means we are somehow out of sync.  So, we reset the
89226996Sgibbs		 * adapter.
89326996Sgibbs		 */
894112734Smdodd		if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) {
895581Srgrimes#ifdef DEBUG
896112734Smdodd			print_rbd(sc->rbuffs[sc->rbhead]);
897581Srgrimes#endif
898179416Sjhb			log(LOG_ERR, "%s: receive descriptors out of sync at %d\n",
899179416Sjhb			    sc->ifp->if_xname, sc->rbhead);
900112734Smdodd			iereset(sc);
90126996Sgibbs			return;
90226996Sgibbs		}
903112734Smdodd		i = sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_LAST;
904581Srgrimes
905112734Smdodd		sc->rbuffs[sc->rbhead]->ie_rbd_length |= IE_RBD_LAST;
906112734Smdodd		sc->rbuffs[sc->rbhead]->ie_rbd_actual = 0;
907112734Smdodd		sc->rbhead = (sc->rbhead + 1) % sc->nrxbufs;
908112734Smdodd		sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
909112734Smdodd		sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs;
91026996Sgibbs	} while (!i);
911581Srgrimes}
912581Srgrimes
913581Srgrimes
914581Srgrimes/*
915581Srgrimes * Start transmission on an interface.
916581Srgrimes */
917798Swollmanstatic void
91826996Sgibbsiestart(struct ifnet *ifp)
919581Srgrimes{
920112734Smdodd	struct	 ie_softc *sc = ifp->if_softc;
921179491Sjhb
922179491Sjhb	IE_LOCK(sc);
923179491Sjhb	iestart_locked(ifp);
924179491Sjhb	IE_UNLOCK(sc);
925179491Sjhb}
926179491Sjhb
927179491Sjhbstatic void
928179491Sjhbiestart_locked(struct ifnet *ifp)
929179491Sjhb{
930179491Sjhb	struct	 ie_softc *sc = ifp->if_softc;
93126996Sgibbs	struct	 mbuf *m0, *m;
93243314Sdillon	volatile unsigned char *buffer;
93326996Sgibbs	u_short	 len;
934581Srgrimes
93526996Sgibbs	/*
93626996Sgibbs	 * This is not really volatile, in this routine, but it makes gcc
93726996Sgibbs	 * happy.
93826996Sgibbs	 */
939112734Smdodd	volatile u_short *bptr = &sc->scb->ie_command_list;
940581Srgrimes
941148887Srwatson	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
94226996Sgibbs		return;
943148887Srwatson	if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
94426996Sgibbs		return;
945581Srgrimes
94626996Sgibbs	do {
947147256Sbrooks		IF_DEQUEUE(&sc->ifp->if_snd, m);
94826996Sgibbs		if (!m)
94926996Sgibbs			break;
950581Srgrimes
951272266Smelifaro		BPF_MTAP(ifp, m);
952272266Smelifaro
953112734Smdodd		buffer = sc->xmit_cbuffs[sc->xmit_count];
95426996Sgibbs		len = 0;
955581Srgrimes
95626996Sgibbs		for (m0 = m; m && len < IE_BUF_LEN; m = m->m_next) {
95726996Sgibbs			bcopy(mtod(m, caddr_t), buffer, m->m_len);
95826996Sgibbs			buffer += m->m_len;
95926996Sgibbs			len += m->m_len;
96026996Sgibbs		}
961581Srgrimes
96226996Sgibbs		m_freem(m0);
96326996Sgibbs		len = max(len, ETHER_MIN_LEN);
96426996Sgibbs
965112734Smdodd		sc->xmit_buffs[sc->xmit_count]->ie_xmit_flags =
96626996Sgibbs		    IE_XMIT_LAST|len;
967112734Smdodd		sc->xmit_buffs[sc->xmit_count]->ie_xmit_next = 0xffff;
968112734Smdodd		sc->xmit_buffs[sc->xmit_count]->ie_xmit_buf =
969112734Smdodd		    MK_24(sc->iomem, sc->xmit_cbuffs[sc->xmit_count]);
970581Srgrimes
971112734Smdodd		sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT;
972112734Smdodd		sc->xmit_cmds[sc->xmit_count]->ie_xmit_status = 0;
973112734Smdodd		sc->xmit_cmds[sc->xmit_count]->ie_xmit_desc =
974112734Smdodd		    MK_16(sc->iomem, sc->xmit_buffs[sc->xmit_count]);
975581Srgrimes
976112734Smdodd		*bptr = MK_16(sc->iomem, sc->xmit_cmds[sc->xmit_count]);
977112734Smdodd		bptr = &sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_link;
978112734Smdodd		sc->xmit_count++;
979112734Smdodd	} while (sc->xmit_count < sc->ntxbufs);
980581Srgrimes
98126996Sgibbs	/*
98226996Sgibbs	 * If we queued up anything for transmission, send it.
98326996Sgibbs	 */
984112734Smdodd	if (sc->xmit_count) {
985112734Smdodd		sc->xmit_cmds[sc->xmit_count - 1]->com.ie_cmd_cmd |=
98626996Sgibbs		    IE_CMD_LAST | IE_CMD_INTR;
987581Srgrimes
98826996Sgibbs		/*
98926996Sgibbs		 * By passing the command pointer as a null, we tell
99026996Sgibbs		 * command_and_wait() to pretend that this isn't an action
99126996Sgibbs		 * command.  I wish I understood what was happening here.
99226996Sgibbs		 */
993112734Smdodd		command_and_wait(sc, IE_CU_START, 0, 0);
994148887Srwatson		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
99526996Sgibbs	}
99626996Sgibbs	return;
997581Srgrimes}
998581Srgrimes
999581Srgrimes/*
1000581Srgrimes * Check to see if there's an 82586 out there.
1001581Srgrimes */
1002112790Smdoddint
1003112790Smdoddcheck_ie_present(struct ie_softc *sc)
1004581Srgrimes{
100526996Sgibbs	volatile struct ie_sys_conf_ptr *scp;
100626996Sgibbs	volatile struct ie_int_sys_conf_ptr *iscp;
100726996Sgibbs	volatile struct ie_sys_ctl_block *scb;
100826996Sgibbs	u_long	realbase;
1009581Srgrimes
1010112790Smdodd	realbase = (uintptr_t) sc->iomembot + sc->iosize  - (1 << 24);
1011581Srgrimes
101238232Sbde	scp = (volatile struct ie_sys_conf_ptr *) (uintptr_t)
101338232Sbde	      (realbase + IE_SCP_ADDR);
101443314Sdillon	bzero((volatile char *) scp, sizeof *scp);
10158876Srgrimes
101626996Sgibbs	/*
101726996Sgibbs	 * First we put the ISCP at the bottom of memory; this tests to make
101826996Sgibbs	 * sure that our idea of the size of memory is the same as the
101926996Sgibbs	 * controller's. This is NOT where the ISCP will be in normal
102026996Sgibbs	 * operation.
102126996Sgibbs	 */
1022112790Smdodd	iscp = (volatile struct ie_int_sys_conf_ptr *) sc->iomembot;
102343314Sdillon	bzero((volatile char *)iscp, sizeof *iscp);
1024581Srgrimes
1025112790Smdodd	scb = (volatile struct ie_sys_ctl_block *) sc->iomembot;
102643314Sdillon	bzero((volatile char *)scb, sizeof *scb);
1027581Srgrimes
1028112734Smdodd	scp->ie_bus_use = sc->bus_use;	/* 8-bit or 16-bit */
102947108Sbde	scp->ie_iscp_ptr = (caddr_t) (uintptr_t)
103047108Sbde	    ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase);
1031581Srgrimes
103226996Sgibbs	iscp->ie_busy = 1;
103326996Sgibbs	iscp->ie_scb_offset = MK_16(realbase, scb) + 256;
1034581Srgrimes
1035112734Smdodd	(*sc->ie_reset_586) (sc);
1036112734Smdodd	(*sc->ie_chan_attn) (sc);
1037581Srgrimes
103826996Sgibbs	DELAY(100);		/* wait a while... */
1039581Srgrimes
104026996Sgibbs	if (iscp->ie_busy) {
104126996Sgibbs		return (0);
104226996Sgibbs	}
104326996Sgibbs	/*
104426996Sgibbs	 * Now relocate the ISCP to its real home, and reset the controller
104526996Sgibbs	 * again.
104626996Sgibbs	 */
104738232Sbde	iscp = (void *) Align((caddr_t) (uintptr_t)
104838232Sbde			      (realbase + IE_SCP_ADDR -
104938232Sbde			       sizeof(struct ie_int_sys_conf_ptr)));
105043314Sdillon	bzero((volatile char *) iscp, sizeof *iscp);	/* ignore cast-qual */
1051581Srgrimes
105247108Sbde	scp->ie_iscp_ptr = (caddr_t) (uintptr_t)
105347108Sbde	    ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase);
1054581Srgrimes
105526996Sgibbs	iscp->ie_busy = 1;
105626996Sgibbs	iscp->ie_scb_offset = MK_16(realbase, scb);
1057581Srgrimes
1058112734Smdodd	(*sc->ie_reset_586) (sc);
1059112734Smdodd	(*sc->ie_chan_attn) (sc);
1060581Srgrimes
106126996Sgibbs	DELAY(100);
1062581Srgrimes
106326996Sgibbs	if (iscp->ie_busy) {
106426996Sgibbs		return (0);
106526996Sgibbs	}
1066112734Smdodd	sc->iomem = (caddr_t) (uintptr_t) realbase;
1067581Srgrimes
1068112734Smdodd	sc->iscp = iscp;
1069112734Smdodd	sc->scb = scb;
1070581Srgrimes
107126996Sgibbs	/*
107226996Sgibbs	 * Acknowledge any interrupts we may have caused...
107326996Sgibbs	 */
1074112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
1075581Srgrimes
107626996Sgibbs	return (1);
1077581Srgrimes}
1078581Srgrimes
1079112790Smdoddvoid
1080112734Smdoddel_reset_586(struct ie_softc *sc)
10812268Sats{
1082112734Smdodd	outb(PORT(sc) + IE507_CTRL, EL_CTRL_RESET);
108326996Sgibbs	DELAY(100);
1084112734Smdodd	outb(PORT(sc) + IE507_CTRL, EL_CTRL_NORMAL);
108526996Sgibbs	DELAY(100);
10862268Sats}
10872268Sats
1088112790Smdoddvoid
1089112734Smdoddsl_reset_586(struct ie_softc *sc)
1090581Srgrimes{
1091112734Smdodd	outb(PORT(sc) + IEATT_RESET, 0);
1092581Srgrimes}
1093581Srgrimes
1094112790Smdoddvoid
1095112734Smdoddee16_reset_586(struct ie_softc *sc)
109624909Sgibbs{
1097112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_586);
109824909Sgibbs	DELAY(100);
1099112734Smdodd	outb(PORT(sc) + IEE16_ECTRL, 0);
110024909Sgibbs	DELAY(100);
110124909Sgibbs}
110224909Sgibbs
1103112790Smdoddvoid
1104112734Smdoddel_chan_attn(struct ie_softc *sc)
11052268Sats{
1106112734Smdodd	outb(PORT(sc) + IE507_ATTN, 1);
11072268Sats}
11082268Sats
1109112790Smdoddvoid
1110112734Smdoddsl_chan_attn(struct ie_softc *sc)
1111581Srgrimes{
1112112734Smdodd	outb(PORT(sc) + IEATT_ATTN, 0);
1113581Srgrimes}
1114581Srgrimes
1115112790Smdoddvoid
1116112734Smdoddee16_chan_attn(struct ie_softc *sc)
111724909Sgibbs{
1118112734Smdodd	outb(PORT(sc) + IEE16_ATTN, 0);
111924909Sgibbs}
112024909Sgibbs
112135210Sbdestatic __inline void
112226996Sgibbsee16_interrupt_enable(struct ie_softc *sc)
112324909Sgibbs{
112424909Sgibbs	DELAY(100);
112524909Sgibbs	outb(sc->port + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE);
112624909Sgibbs	DELAY(100);
112724909Sgibbs}
112824909Sgibbs
1129112790Smdoddvoid
1130112734Smdoddsl_read_ether(struct ie_softc *sc, unsigned char *addr)
1131581Srgrimes{
113226996Sgibbs	int	i;
1133581Srgrimes
113426996Sgibbs	for (i = 0; i < 6; i++)
1135112734Smdodd		addr[i] = inb(PORT(sc) + i);
1136581Srgrimes}
1137581Srgrimes
1138798Swollmanstatic void
1139112734Smdoddiereset(struct ie_softc *sc)
1140581Srgrimes{
1141179416Sjhb	struct ifnet *ifp = sc->ifp;
1142581Srgrimes
1143179416Sjhb	if_printf(ifp, "reset\n");
1144179491Sjhb	ie_stop(sc);
1145581Srgrimes
114626996Sgibbs	/*
114726996Sgibbs	 * Stop i82586 dead in its tracks.
114826996Sgibbs	 */
1149112734Smdodd	if (command_and_wait(sc, IE_RU_ABORT | IE_CU_ABORT, 0, 0))
1150179416Sjhb		if_printf(ifp, "abort commands timed out\n");
1151581Srgrimes
1152112734Smdodd	if (command_and_wait(sc, IE_RU_DISABLE | IE_CU_STOP, 0, 0))
1153179416Sjhb		if_printf(ifp, "disable commands timed out\n");
1154581Srgrimes
1155581Srgrimes#ifdef notdef
1156112790Smdodd	if (!check_ie_present(sc))
115726996Sgibbs		panic("ie disappeared!");
1158581Srgrimes#endif
1159581Srgrimes
1160179491Sjhb	if (ifp->if_flags & IFF_UP)
1161179491Sjhb		ieinit_locked(sc);
11628876Srgrimes
116326996Sgibbs	return;
1164581Srgrimes}
1165581Srgrimes
1166581Srgrimes/*
1167581Srgrimes * Send a command to the controller and wait for it to either
1168581Srgrimes * complete or be accepted, depending on the command.  If the
1169581Srgrimes * command pointer is null, then pretend that the command is
1170581Srgrimes * not an action command.  If the command pointer is not null,
1171581Srgrimes * and the command is an action command, wait for
1172581Srgrimes * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK
1173581Srgrimes * to become true.
1174581Srgrimes */
117526996Sgibbsstatic int
1176112734Smdoddcommand_and_wait(struct ie_softc *sc, int cmd, volatile void *pcmd, int mask)
1177581Srgrimes{
117826996Sgibbs	volatile struct ie_cmd_common *cc = pcmd;
1179179491Sjhb	int i;
1180581Srgrimes
1181112734Smdodd	sc->scb->ie_command = (u_short) cmd;
11828876Srgrimes
118326996Sgibbs	if (IE_ACTION_COMMAND(cmd) && pcmd) {
1184112734Smdodd		(*sc->ie_chan_attn) (sc);
1185179491Sjhb
118626996Sgibbs		/*
118726996Sgibbs		 * Now spin-lock waiting for status.  This is not a very
118826996Sgibbs		 * nice thing to do, but I haven't figured out how, or
118926996Sgibbs		 * indeed if, we can put the process waiting for action to
119026996Sgibbs		 * sleep.  (We may be getting called through some other
119126996Sgibbs		 * timeout running in the kernel.)
1192179491Sjhb		 *
1193179491Sjhb		 * According to the packet driver, the minimum timeout
1194179491Sjhb		 * should be .369 seconds, which we round up to .37.
119526996Sgibbs		 */
1196179491Sjhb		for (i = 0; i < 370; i++) {
1197179491Sjhb			if (cc->ie_cmd_status & mask)
1198179491Sjhb				return (0);
1199179491Sjhb			DELAY(1000);
120026996Sgibbs		}
1201581Srgrimes
1202179491Sjhb		return (1);
120326996Sgibbs	} else {
12048876Srgrimes
120526996Sgibbs		/*
120626996Sgibbs		 * Otherwise, just wait for the command to be accepted.
120726996Sgibbs		 */
1208112734Smdodd		(*sc->ie_chan_attn) (sc);
1209581Srgrimes
1210112734Smdodd		while (sc->scb->ie_command);	/* spin lock */
1211581Srgrimes
121226996Sgibbs		return (0);
121326996Sgibbs	}
1214581Srgrimes}
1215581Srgrimes
1216581Srgrimes/*
1217581Srgrimes * Run the time-domain reflectometer...
1218581Srgrimes */
121926996Sgibbsstatic void
1220112734Smdoddrun_tdr(struct ie_softc *sc, volatile struct ie_tdr_cmd *cmd)
1221581Srgrimes{
122226996Sgibbs	int	result;
1223581Srgrimes
122426996Sgibbs	cmd->com.ie_cmd_status = 0;
122526996Sgibbs	cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST;
122626996Sgibbs	cmd->com.ie_cmd_link = 0xffff;
122726996Sgibbs	cmd->ie_tdr_time = 0;
1228581Srgrimes
1229112734Smdodd	sc->scb->ie_command_list = MK_16(MEM(sc), cmd);
123026996Sgibbs	cmd->ie_tdr_time = 0;
1231581Srgrimes
1232112734Smdodd	if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL))
123326996Sgibbs		result = 0x2000;
123426996Sgibbs	else
123526996Sgibbs		result = cmd->ie_tdr_time;
1236581Srgrimes
1237112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
1238581Srgrimes
123926996Sgibbs	if (result & IE_TDR_SUCCESS)
124026996Sgibbs		return;
1241581Srgrimes
124226996Sgibbs	if (result & IE_TDR_XCVR) {
1243179416Sjhb		if_printf(sc->ifp, "transceiver problem\n");
124426996Sgibbs	} else if (result & IE_TDR_OPEN) {
1245179416Sjhb		if_printf(sc->ifp, "TDR detected an open %d clocks away\n",
124626996Sgibbs		       result & IE_TDR_TIME);
124726996Sgibbs	} else if (result & IE_TDR_SHORT) {
1248179416Sjhb		if_printf(sc->ifp, "TDR detected a short %d clocks away\n",
124926996Sgibbs		       result & IE_TDR_TIME);
125026996Sgibbs	} else {
1251179416Sjhb		if_printf(sc->ifp, "TDR returned unknown status %x\n", result);
125226996Sgibbs	}
1253581Srgrimes}
1254581Srgrimes
125526996Sgibbsstatic void
1256112734Smdoddstart_receiver(struct ie_softc *sc)
1257581Srgrimes{
1258581Srgrimes
1259112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
1260112734Smdodd	command_and_wait(sc, IE_RU_START, 0, 0);
1261581Srgrimes
1262112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
1263581Srgrimes}
1264581Srgrimes
1265581Srgrimes/*
1266581Srgrimes * Here is a helper routine for iernr() and ieinit().  This sets up
1267581Srgrimes * the RFA.
1268581Srgrimes */
126943314Sdillonstatic v_caddr_t
1270112734Smdoddsetup_rfa(struct ie_softc *sc, v_caddr_t ptr)
127126996Sgibbs{
127243314Sdillon	volatile struct ie_recv_frame_desc *rfd = (volatile void *)ptr;
127326996Sgibbs	volatile struct ie_recv_buf_desc *rbd;
127426996Sgibbs	int	i;
1275581Srgrimes
127626996Sgibbs	/* First lay them out */
1277112734Smdodd	for (i = 0; i < sc->nframes; i++) {
1278112734Smdodd		sc->rframes[i] = rfd;
127943314Sdillon		bzero((volatile char *) rfd, sizeof *rfd);	/* ignore cast-qual */
128026996Sgibbs		rfd++;
128126996Sgibbs	}
1282581Srgrimes
128347108Sbde	ptr = Alignvol(rfd);		/* ignore cast-qual */
12848876Srgrimes
128526996Sgibbs	/* Now link them together */
1286112734Smdodd	for (i = 0; i < sc->nframes; i++) {
1287112734Smdodd		sc->rframes[i]->ie_fd_next =
1288112734Smdodd		    MK_16(MEM(sc), sc->rframes[(i + 1) % sc->nframes]);
128926996Sgibbs	}
12908876Srgrimes
129126996Sgibbs	/* Finally, set the EOL bit on the last one. */
1292112734Smdodd	sc->rframes[sc->nframes - 1]->ie_fd_last |= IE_FD_LAST;
1293581Srgrimes
129426996Sgibbs	/*
129526996Sgibbs	 * Now lay out some buffers for the incoming frames.  Note that we
129626996Sgibbs	 * set aside a bit of slop in each buffer, to make sure that we have
129726996Sgibbs	 * enough space to hold a single frame in every buffer.
129826996Sgibbs	 */
129943314Sdillon	rbd = (volatile void *) ptr;
1300581Srgrimes
1301112734Smdodd	for (i = 0; i < sc->nrxbufs; i++) {
1302112734Smdodd		sc->rbuffs[i] = rbd;
130343314Sdillon		bzero((volatile char *)rbd, sizeof *rbd);
130447108Sbde		ptr = Alignvol(ptr + sizeof *rbd);
130526996Sgibbs		rbd->ie_rbd_length = IE_RBUF_SIZE;
1306112734Smdodd		rbd->ie_rbd_buffer = MK_24(MEM(sc), ptr);
1307112734Smdodd		sc->cbuffs[i] = (volatile void *) ptr;
130826996Sgibbs		ptr += IE_RBUF_SIZE;
130943314Sdillon		rbd = (volatile void *) ptr;
131026996Sgibbs	}
13118876Srgrimes
131226996Sgibbs	/* Now link them together */
1313112734Smdodd	for (i = 0; i < sc->nrxbufs; i++) {
1314112734Smdodd		sc->rbuffs[i]->ie_rbd_next =
1315112734Smdodd		    MK_16(MEM(sc), sc->rbuffs[(i + 1) % sc->nrxbufs]);
131626996Sgibbs	}
13178876Srgrimes
131826996Sgibbs	/* Tag EOF on the last one */
1319112734Smdodd	sc->rbuffs[sc->nrxbufs - 1]->ie_rbd_length |= IE_RBD_LAST;
1320581Srgrimes
132126996Sgibbs	/*
132226996Sgibbs	 * We use the head and tail pointers on receive to keep track of the
132326996Sgibbs	 * order in which RFDs and RBDs are used.
132426996Sgibbs	 */
1325112734Smdodd	sc->rfhead = 0;
1326112734Smdodd	sc->rftail = sc->nframes - 1;
1327112734Smdodd	sc->rbhead = 0;
1328112734Smdodd	sc->rbtail = sc->nrxbufs - 1;
1329581Srgrimes
1330112734Smdodd	sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]);
1331112734Smdodd	sc->rframes[0]->ie_fd_buf_desc = MK_16(MEM(sc), sc->rbuffs[0]);
1332581Srgrimes
133347108Sbde	ptr = Alignvol(ptr);
133426996Sgibbs	return (ptr);
1335581Srgrimes}
1336581Srgrimes
1337581Srgrimes/*
1338581Srgrimes * Run the multicast setup command.
1339581Srgrimes */
134026996Sgibbsstatic int
1341112734Smdoddmc_setup(struct ie_softc *sc)
134226996Sgibbs{
1343112734Smdodd	volatile struct ie_mcast_cmd *cmd = (volatile void *)sc->xmit_cbuffs[0];
13448876Srgrimes
134526996Sgibbs	cmd->com.ie_cmd_status = 0;
134626996Sgibbs	cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST;
134726996Sgibbs	cmd->com.ie_cmd_link = 0xffff;
13488876Srgrimes
134926996Sgibbs	/* ignore cast-qual */
1350112734Smdodd	bcopy((v_caddr_t) sc->mcast_addrs, (v_caddr_t) cmd->ie_mcast_addrs,
1351112734Smdodd	      sc->mcast_count * sizeof *sc->mcast_addrs);
1352581Srgrimes
1353112734Smdodd	cmd->ie_mcast_bytes = sc->mcast_count * 6;	/* grrr... */
13548876Srgrimes
1355112734Smdodd	sc->scb->ie_command_list = MK_16(MEM(sc), cmd);
1356112734Smdodd	if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)
135726996Sgibbs	    || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
1358179416Sjhb		if_printf(sc->ifp, "multicast address setup command failed\n");
135926996Sgibbs		return (0);
136026996Sgibbs	}
136126996Sgibbs	return (1);
1362581Srgrimes}
1363581Srgrimes
1364581Srgrimes/*
1365581Srgrimes * This routine takes the environment generated by check_ie_present()
1366581Srgrimes * and adds to it all the other structures we need to operate the adapter.
1367581Srgrimes * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands,
1368581Srgrimes * starting the receiver unit, and clearing interrupts.
1369581Srgrimes */
1370798Swollmanstatic void
137150084Smdoddieinit(xsc)
137250084Smdodd	void *xsc;
1373581Srgrimes{
1374112734Smdodd	struct ie_softc *sc = xsc;
1375179491Sjhb
1376179491Sjhb	IE_LOCK(sc);
1377179491Sjhb	ieinit_locked(sc);
1378179491Sjhb	IE_UNLOCK(sc);
1379179491Sjhb}
1380179491Sjhb
1381179491Sjhbstatic void
1382179491Sjhbieinit_locked(struct ie_softc *sc)
1383179491Sjhb{
1384179416Sjhb	struct ifnet *ifp = sc->ifp;
1385112734Smdodd	volatile struct ie_sys_ctl_block *scb = sc->scb;
1386112734Smdodd	caddr_t ptr;
138726996Sgibbs	int	i;
1388581Srgrimes
138947108Sbde	ptr = Alignvol((volatile char *) scb + sizeof *scb);
1390581Srgrimes
139126996Sgibbs	/*
139226996Sgibbs	 * Send the configure command first.
139326996Sgibbs	 */
139426996Sgibbs	{
139543314Sdillon		volatile struct ie_config_cmd *cmd = (volatile void *) ptr;
1396581Srgrimes
1397112734Smdodd		ie_setup_config(cmd, sc->promisc,
1398112734Smdodd				sc->hard_type == IE_STARLAN10);
139926996Sgibbs		cmd->com.ie_cmd_status = 0;
140026996Sgibbs		cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST;
140126996Sgibbs		cmd->com.ie_cmd_link = 0xffff;
1402581Srgrimes
1403112734Smdodd		scb->ie_command_list = MK_16(MEM(sc), cmd);
1404581Srgrimes
1405112734Smdodd		if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)
140626996Sgibbs		 || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
1407179416Sjhb			if_printf(ifp, "configure command failed\n");
140826996Sgibbs			return;
140926996Sgibbs		}
141026996Sgibbs	}
141126996Sgibbs	/*
141226996Sgibbs	 * Now send the Individual Address Setup command.
141326996Sgibbs	 */
141426996Sgibbs	{
141543314Sdillon		volatile struct ie_iasetup_cmd *cmd = (volatile void *) ptr;
1416581Srgrimes
141726996Sgibbs		cmd->com.ie_cmd_status = 0;
141826996Sgibbs		cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST;
141926996Sgibbs		cmd->com.ie_cmd_link = 0xffff;
1420581Srgrimes
1421179416Sjhb		bcopy((volatile char *)IF_LLADDR(ifp),
142243314Sdillon		      (volatile char *)&cmd->ie_address, sizeof cmd->ie_address);
1423112734Smdodd		scb->ie_command_list = MK_16(MEM(sc), cmd);
1424112734Smdodd		if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)
142526996Sgibbs		    || !(cmd->com.ie_cmd_status & IE_STAT_OK)) {
1426179416Sjhb			if_printf(ifp, "individual address "
1427179416Sjhb			       "setup command failed\n");
142826996Sgibbs			return;
142926996Sgibbs		}
143026996Sgibbs	}
1431581Srgrimes
143226996Sgibbs	/*
143326996Sgibbs	 * Now run the time-domain reflectometer.
143426996Sgibbs	 */
1435112734Smdodd	run_tdr(sc, (volatile void *) ptr);
1436581Srgrimes
143726996Sgibbs	/*
143826996Sgibbs	 * Acknowledge any interrupts we have generated thus far.
143926996Sgibbs	 */
1440112734Smdodd	ie_ack(sc, IE_ST_WHENCE);
1441581Srgrimes
144226996Sgibbs	/*
144326996Sgibbs	 * Set up the RFA.
144426996Sgibbs	 */
1445112734Smdodd	ptr = setup_rfa(sc, ptr);
1446581Srgrimes
144726996Sgibbs	/*
144826996Sgibbs	 * Finally, the transmit command and buffer are the last little bit
144926996Sgibbs	 * of work.
145026996Sgibbs	 */
1451581Srgrimes
145226996Sgibbs	/* transmit command buffers */
1453112734Smdodd	for (i = 0; i < sc->ntxbufs; i++) {
1454112734Smdodd		sc->xmit_cmds[i] = (volatile void *) ptr;
1455112734Smdodd		ptr += sizeof *sc->xmit_cmds[i];
145647108Sbde		ptr = Alignvol(ptr);
1457112734Smdodd		sc->xmit_buffs[i] = (volatile void *)ptr;
1458112734Smdodd		ptr += sizeof *sc->xmit_buffs[i];
145947108Sbde		ptr = Alignvol(ptr);
146026996Sgibbs	}
1461581Srgrimes
146226996Sgibbs	/* transmit buffers */
1463112734Smdodd	for (i = 0; i < sc->ntxbufs - 1; i++) {
1464112734Smdodd		sc->xmit_cbuffs[i] = (volatile void *)ptr;
146526996Sgibbs		ptr += IE_BUF_LEN;
146647108Sbde		ptr = Alignvol(ptr);
146726996Sgibbs	}
1468112734Smdodd	sc->xmit_cbuffs[sc->ntxbufs - 1] = (volatile void *) ptr;
1469581Srgrimes
1470112734Smdodd	for (i = 1; i < sc->ntxbufs; i++) {
1471112734Smdodd		bzero((v_caddr_t) sc->xmit_cmds[i], sizeof *sc->xmit_cmds[i]);
1472112734Smdodd		bzero((v_caddr_t) sc->xmit_buffs[i], sizeof *sc->xmit_buffs[i]);
147326996Sgibbs	}
1474581Srgrimes
147526996Sgibbs	/*
147626996Sgibbs	 * This must be coordinated with iestart() and ietint().
147726996Sgibbs	 */
1478112734Smdodd	sc->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL;
1479581Srgrimes
148026996Sgibbs	/* take the ee16 out of loopback */
1481112734Smdodd	if (sc->hard_type == IE_EE16) {
148226996Sgibbs		u_int8_t bart_config;
1483581Srgrimes
1484112734Smdodd		bart_config = inb(PORT(sc) + IEE16_CONFIG);
148526996Sgibbs		bart_config &= ~IEE16_BART_LOOPBACK;
148626996Sgibbs		/* inb doesn't get bit! */
148726996Sgibbs		bart_config |= IEE16_BART_MCS16_TEST;
1488112734Smdodd		outb(PORT(sc) + IEE16_CONFIG, bart_config);
1489112734Smdodd		ee16_interrupt_enable(sc);
1490112734Smdodd		ee16_chan_attn(sc);
149126996Sgibbs	}
1492179416Sjhb	ifp->if_drv_flags |= IFF_DRV_RUNNING;	/* tell higher levels
149326996Sgibbs							 * we're here */
1494179416Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1495112788Smdodd
1496112734Smdodd	start_receiver(sc);
149725971Sgibbs
149826996Sgibbs	return;
1499581Srgrimes}
1500581Srgrimes
150126996Sgibbsstatic void
1502112734Smdoddie_stop(struct ie_softc *sc)
1503581Srgrimes{
1504179491Sjhb	struct ifnet *ifp = sc->ifp;
1505179491Sjhb
1506179491Sjhb	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
1507112734Smdodd	command_and_wait(sc, IE_RU_DISABLE, 0, 0);
1508581Srgrimes}
1509581Srgrimes
1510798Swollmanstatic int
151136735Sdfrieioctl(struct ifnet *ifp, u_long command, caddr_t data)
1512581Srgrimes{
1513179491Sjhb	int	error = 0;
1514112734Smdodd	struct	 ie_softc *sc = ifp->if_softc;
1515581Srgrimes
151626996Sgibbs	switch (command) {
151726996Sgibbs	case SIOCSIFFLAGS:
151826996Sgibbs		/*
151926996Sgibbs		 * Note that this device doesn't have an "all multicast"
152026996Sgibbs		 * mode, so we must turn on promiscuous mode and do the
152126996Sgibbs		 * filtering manually.
152226996Sgibbs		 */
1523179491Sjhb		IE_LOCK(sc);
152426996Sgibbs		if ((ifp->if_flags & IFF_UP) == 0 &&
1525148887Srwatson		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1526112734Smdodd			ie_stop(sc);
152726996Sgibbs		} else if ((ifp->if_flags & IFF_UP) &&
1528148887Srwatson			   (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1529112734Smdodd			sc->promisc =
153026996Sgibbs			    ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
1531179558Sjhb			ieinit_locked(sc);
1532112734Smdodd		} else if (sc->promisc ^
153326996Sgibbs			   (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) {
1534112734Smdodd			sc->promisc =
153526996Sgibbs			    ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
1536179558Sjhb			ieinit_locked(sc);
153726996Sgibbs		}
1538179491Sjhb		IE_UNLOCK(sc);
153926996Sgibbs		break;
1540581Srgrimes
154126996Sgibbs	case SIOCADDMULTI:
154226996Sgibbs	case SIOCDELMULTI:
154326996Sgibbs		/*
154426996Sgibbs		 * Update multicast listeners
154526996Sgibbs		 */
154626996Sgibbs		/* reset multicast filtering */
1547179491Sjhb		IE_LOCK(sc);
1548112734Smdodd		ie_mc_reset(sc);
1549179491Sjhb		IE_UNLOCK(sc);
155026996Sgibbs		error = 0;
155126996Sgibbs		break;
1552581Srgrimes
155326996Sgibbs	default:
1554106937Ssam		error = ether_ioctl(ifp, command, data);
1555106937Ssam		break;
155626996Sgibbs	}
1557581Srgrimes
155826996Sgibbs	return (error);
1559581Srgrimes}
1560581Srgrimes
156126996Sgibbsstatic void
1562112734Smdoddie_mc_reset(struct ie_softc *sc)
156326996Sgibbs{
156426996Sgibbs	struct ifmultiaddr *ifma;
1565581Srgrimes
156626996Sgibbs	/*
156726996Sgibbs	 * Step through the list of addresses.
156826996Sgibbs	 */
1569112734Smdodd	sc->mcast_count = 0;
1570195049Srwatson	if_maddr_rlock(sc->ifp);
1571147256Sbrooks	TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
157226996Sgibbs		if (ifma->ifma_addr->sa_family != AF_LINK)
157326996Sgibbs			continue;
1574581Srgrimes
157526996Sgibbs		/* XXX - this is broken... */
1576112734Smdodd		if (sc->mcast_count >= MAXMCAST) {
1577147256Sbrooks			sc->ifp->if_flags |= IFF_ALLMULTI;
1578179491Sjhb			if (sc->ifp->if_flags & IFF_UP)
1579179491Sjhb				ieinit_locked(sc);
158026996Sgibbs			goto setflag;
158126996Sgibbs		}
158226996Sgibbs		bcopy(LLADDR((struct sockaddr_dl *) ifma->ifma_addr),
1583112734Smdodd		      &(sc->mcast_addrs[sc->mcast_count]), 6);
1584112734Smdodd		sc->mcast_count++;
158526996Sgibbs	}
1586195049Srwatson	if_maddr_runlock(sc->ifp);
158721666Swollman
1588581Srgrimessetflag:
1589112734Smdodd	sc->want_mcsetup = 1;
1590581Srgrimes}
1591581Srgrimes
1592581Srgrimes
1593581Srgrimes#ifdef DEBUG
159433181Seivindstatic void
159526996Sgibbsprint_rbd(volatile struct ie_recv_buf_desc * rbd)
159626996Sgibbs{
159738224Sbde	printf("RBD at %p:\n"
159838224Sbde	       "actual %04x, next %04x, buffer %p\n"
159926996Sgibbs	       "length %04x, mbz %04x\n",
160043314Sdillon	       (volatile void *) rbd,
160137618Sbde	       rbd->ie_rbd_actual, rbd->ie_rbd_next,
160237618Sbde	       (void *) rbd->ie_rbd_buffer,
160326996Sgibbs	       rbd->ie_rbd_length, rbd->mbz);
1604581Srgrimes}
1605581Srgrimes
160626996Sgibbs#endif				/* DEBUG */
1607112790Smdodd
1608112790Smdoddint
1609112790Smdoddie_alloc_resources (device_t dev)
1610112790Smdodd{
1611112790Smdodd	struct ie_softc *       sc;
1612112790Smdodd	int                     error;
1613112790Smdodd
1614112790Smdodd	error = 0;
1615112790Smdodd	sc = device_get_softc(dev);
1616112790Smdodd
1617127135Snjl	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
1618127135Snjl					    RF_ACTIVE);
1619112790Smdodd	if (!sc->io_res) {
1620112790Smdodd		device_printf(dev, "No I/O space?!\n");
1621112790Smdodd		error = ENOMEM;
1622112790Smdodd		goto bad;
1623112790Smdodd	}
1624112790Smdodd	sc->io_bt = rman_get_bustag(sc->io_res);
1625112790Smdodd	sc->io_bh = rman_get_bushandle(sc->io_res);
1626112790Smdodd
1627127135Snjl	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
1628127135Snjl					     RF_ACTIVE);
1629112790Smdodd	if (!sc->mem_res) {
1630112790Smdodd                device_printf(dev, "No Memory!\n");
1631112790Smdodd		error = ENOMEM;
1632112790Smdodd		goto bad;
1633112790Smdodd	}
1634112790Smdodd	sc->mem_bt = rman_get_bustag(sc->mem_res);
1635112790Smdodd	sc->mem_bh = rman_get_bushandle(sc->mem_res);
1636112790Smdodd
1637127135Snjl	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
1638127135Snjl					     RF_ACTIVE);
1639112790Smdodd	if (!sc->irq_res) {
1640112790Smdodd		device_printf(dev, "No IRQ!\n");
1641112790Smdodd		error = ENOMEM;
1642112790Smdodd		goto bad;
1643112790Smdodd	}
1644112790Smdodd
1645112790Smdodd	sc->port = rman_get_start(sc->io_res);  /* XXX hack */
1646112790Smdodd	sc->iomembot = rman_get_virtual(sc->mem_res);
1647112790Smdodd	sc->iosize = rman_get_size(sc->mem_res);
1648112790Smdodd
1649112790Smdodd	return (0);
1650112790Smdoddbad:
1651112790Smdodd	return (error);
1652112790Smdodd}
1653112790Smdodd
1654112790Smdoddvoid
1655112790Smdoddie_release_resources (device_t dev)
1656112790Smdodd{
1657112790Smdodd	struct ie_softc *       sc;
1658112790Smdodd
1659112790Smdodd	sc = device_get_softc(dev);
1660112790Smdodd
1661112790Smdodd	if (sc->irq_ih)
1662112790Smdodd		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
1663179491Sjhb	if (sc->rframes)
1664179491Sjhb		free(sc->rframes, M_DEVBUF);
1665112790Smdodd	if (sc->io_res)
1666112790Smdodd		bus_release_resource(dev, SYS_RES_IOPORT,
1667112790Smdodd				     sc->io_rid, sc->io_res);
1668112790Smdodd	if (sc->irq_res)
1669112790Smdodd		bus_release_resource(dev, SYS_RES_IRQ,
1670112790Smdodd				     sc->irq_rid, sc->irq_res);
1671112790Smdodd	if (sc->mem_res)
1672112790Smdodd		bus_release_resource(dev, SYS_RES_MEMORY,
1673112790Smdodd				     sc->mem_rid, sc->mem_res);
1674150215Sru	if (sc->ifp)
1675150215Sru		if_free(sc->ifp);
1676112790Smdodd
1677112790Smdodd	return;
1678112790Smdodd}
1679112790Smdodd
1680112790Smdoddint
1681112790Smdoddie_detach (device_t dev)
1682112790Smdodd{
1683112790Smdodd	struct ie_softc *	sc;
1684112790Smdodd	struct ifnet *		ifp;
1685112790Smdodd
1686112790Smdodd	sc = device_get_softc(dev);
1687147256Sbrooks	ifp = sc->ifp;
1688112790Smdodd
1689179491Sjhb	IE_LOCK(sc);
1690112790Smdodd	if (sc->hard_type == IE_EE16)
1691181134Sjhb		ee16_shutdown(sc);
1692112790Smdodd
1693112790Smdodd	ie_stop(sc);
1694179491Sjhb	IE_UNLOCK(sc);
1695112790Smdodd	ether_ifdetach(ifp);
1696112790Smdodd	ie_release_resources(dev);
1697179491Sjhb	mtx_destroy(&sc->lock);
1698112790Smdodd
1699112790Smdodd	return (0);
1700112790Smdodd}
1701