if_ex.c revision 57989
1189499Srnoland/*
2189499Srnoland * Copyright (c) 1996, Javier Mart�n Rueda (jmrueda@diatel.upm.es)
3189499Srnoland * All rights reserved.
4189499Srnoland *
5189499Srnoland * Redistribution and use in source and binary forms, with or without
6189499Srnoland * modification, are permitted provided that the following conditions
7189499Srnoland * are met:
8189499Srnoland * 1. Redistributions of source code must retain the above copyright
9189499Srnoland *    notice unmodified, this list of conditions, and the following
10189499Srnoland *    disclaimer.
11189499Srnoland * 2. Redistributions in binary form must reproduce the above copyright
12189499Srnoland *    notice, this list of conditions and the following disclaimer in the
13189499Srnoland *    documentation and/or other materials provided with the distribution.
14189499Srnoland *
15189499Srnoland * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16189499Srnoland * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17189499Srnoland * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18189499Srnoland * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19189499Srnoland * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20189499Srnoland * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21189499Srnoland * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22189499Srnoland * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23189499Srnoland * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24189499Srnoland * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25189499Srnoland * SUCH DAMAGE.
26189499Srnoland *
27189499Srnoland * $FreeBSD: head/sys/dev/ex/if_ex.c 57989 2000-03-13 12:27:21Z mdodd $
28189499Srnoland *
29189499Srnoland * MAINTAINER: Matthew N. Dodd <winter@jurai.net>
30189499Srnoland *                             <mdodd@FreeBSD.org>
31189499Srnoland */
32189499Srnoland
33189499Srnoland/*
34189499Srnoland * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver
35189499Srnoland *
36189499Srnoland * Revision history:
37189499Srnoland *
38189499Srnoland * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast.
39189499Srnoland */
40189499Srnoland
41189499Srnoland#include "ex.h"
42189499Srnoland
43189499Srnoland#include <sys/param.h>
44189499Srnoland#include <sys/systm.h>
45189499Srnoland#include <sys/kernel.h>
46189499Srnoland#include <sys/conf.h>
47189499Srnoland#include <sys/sockio.h>
48189499Srnoland#include <sys/mbuf.h>
49189499Srnoland#include <sys/socket.h>
50189499Srnoland
51189499Srnoland#include <sys/module.h>
52189499Srnoland#include <sys/bus.h>
53189499Srnoland
54189499Srnoland#include <machine/bus.h>
55189499Srnoland#include <machine/resource.h>
56189499Srnoland#include <sys/rman.h>
57189499Srnoland
58189499Srnoland#include <net/if.h>
59189499Srnoland#include <net/if_arp.h>
60189499Srnoland#include <net/if_media.h>
61189499Srnoland#include <net/ethernet.h>
62189499Srnoland#include <net/bpf.h>
63189499Srnoland
64189499Srnoland#include <netinet/in.h>
65189499Srnoland#include <netinet/if_ether.h>
66189499Srnoland
67189499Srnoland#include <machine/clock.h>
68189499Srnoland
69189499Srnoland#include <isa/isavar.h>
70189499Srnoland#include <isa/pnpvar.h>
71189499Srnoland
72189499Srnoland#include <dev/ex/if_exreg.h>
73189499Srnoland
74189499Srnoland#ifdef EXDEBUG
75189499Srnoland# define Start_End 1
76189499Srnoland# define Rcvd_Pkts 2
77189499Srnoland# define Sent_Pkts 4
78189499Srnoland# define Status    8
79189499Srnolandstatic int debug_mask = 0;
80189499Srnolandstatic int exintr_count = 0;
81189499Srnoland# define DODEBUG(level, action) if (level & debug_mask) action
82189499Srnoland#else
83189499Srnoland# define DODEBUG(level, action)
84189499Srnoland#endif
85189499Srnoland
86189499Srnoland#define CARD_TYPE_EX_10		1
87189499Srnoland#define CARD_TYPE_EX_10_PLUS	2
88189499Srnoland
89189499Srnolandstruct ex_softc {
90189499Srnoland  	struct arpcom	arpcom;		/* Ethernet common data */
91189499Srnoland	struct ifmedia	ifmedia;
92189499Srnoland
93189499Srnoland	device_t	dev;
94189499Srnoland	struct resource *ioport;
95189499Srnoland	struct resource *irq;
96189499Srnoland
97189499Srnoland	u_int		iobase;		/* I/O base address. */
98189499Srnoland	u_short		irq_no;		/* IRQ number. */
99189499Srnoland
100189499Srnoland	char *		irq2ee;		/* irq <-> internal		*/
101189499Srnoland	u_char *	ee2irq;		/* representation conversion	*/
102189499Srnoland
103189499Srnoland	u_int		mem_size;	/* Total memory size, in bytes. */
104189499Srnoland	u_int		rx_mem_size;	/* Rx memory size (by default,	*/
105189499Srnoland					/* first 3/4 of total memory).	*/
106189499Srnoland
107189499Srnoland	u_int		rx_lower_limit;	/* Lower and upper limits of	*/
108189499Srnoland	u_int		rx_upper_limit;	/* receive buffer.		*/
109189499Srnoland
110189499Srnoland	u_int		rx_head;	/* Head of receive ring buffer. */
111189499Srnoland	u_int		tx_mem_size;	/* Tx memory size (by default,	*/
112189499Srnoland					/* last quarter of total memory).*/
113189499Srnoland
114189499Srnoland	u_int		tx_lower_limit;	/* Lower and upper limits of	*/
115189499Srnoland	u_int		tx_upper_limit;	/* transmit buffer.		*/
116189499Srnoland
117189499Srnoland	u_int		tx_head;	/* Head and tail of 		*/
118189499Srnoland	u_int		tx_tail;	/* transmit ring buffer.	*/
119189499Srnoland
120189499Srnoland	u_int		tx_last;	/* Pointer to beginning of last	*/
121189499Srnoland					/* frame in the chain.		*/
122189499Srnoland};
123189499Srnoland
124189499Srnolandstatic char irq2eemap[] =
125189499Srnoland	{ -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 };
126189499Srnolandstatic u_char ee2irqmap[] =
127189499Srnoland	{ 9, 3, 5, 10, 11, 0, 0, 0 };
128189499Srnoland
129189499Srnolandstatic char plus_irq2eemap[] =
130189499Srnoland	{ -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 };
131189499Srnolandstatic u_char plus_ee2irqmap[] =
132189499Srnoland	{ 3, 4, 5, 7, 9, 10, 11, 12 };
133189499Srnoland
134189499Srnoland/* Bus Front End Functions */
135189499Srnolandstatic void	ex_isa_identify	__P((driver_t *, device_t));
136189499Srnolandstatic int	ex_isa_probe	__P((device_t));
137189499Srnolandstatic int	ex_isa_attach	__P((device_t));
138189499Srnoland
139189499Srnoland/* Network Interface Functions */
140189499Srnolandstatic void	ex_init		__P((void *));
141189499Srnolandstatic void	ex_start	__P((struct ifnet *));
142189499Srnolandstatic int	ex_ioctl	__P((struct ifnet *, u_long, caddr_t));
143189499Srnolandstatic void	ex_watchdog	__P((struct ifnet *));
144189499Srnoland
145189499Srnoland/* ifmedia Functions	*/
146189499Srnolandstatic int	ex_ifmedia_upd	__P((struct ifnet *));
147189499Srnolandstatic void	ex_ifmedia_sts	__P((struct ifnet *, struct ifmediareq *));
148189499Srnoland
149189499Srnolandstatic void	ex_stop		__P((struct ex_softc *));
150189499Srnolandstatic void	ex_reset	__P((struct ex_softc *));
151189499Srnoland
152189499Srnolandstatic driver_intr_t	ex_intr;
153189499Srnolandstatic void	ex_tx_intr	__P((struct ex_softc *));
154189499Srnolandstatic void	ex_rx_intr	__P((struct ex_softc *));
155189499Srnoland
156189499Srnolandstatic u_short	eeprom_read	__P((int, int));
157189499Srnoland
158189499Srnolandstatic device_method_t ex_methods[] = {
159189499Srnoland	/* Device interface */
160189499Srnoland	DEVMETHOD(device_identify,	ex_isa_identify),
161189499Srnoland	DEVMETHOD(device_probe,		ex_isa_probe),
162189499Srnoland	DEVMETHOD(device_attach,	ex_isa_attach),
163189499Srnoland
164189499Srnoland	{ 0, 0 }
165189499Srnoland};
166189499Srnoland
167189499Srnolandstatic driver_t ex_driver = {
168189499Srnoland	"ex",
169189499Srnoland	ex_methods,
170189499Srnoland	sizeof(struct ex_softc),
171189499Srnoland};
172189499Srnoland
173189499Srnolandstatic devclass_t ex_devclass;
174189499Srnoland
175189499SrnolandDRIVER_MODULE(ex, isa, ex_driver, ex_devclass, 0, 0);
176189499Srnoland
177189499Srnolandstatic struct isa_pnp_id ex_ids[] = {
178189499Srnoland	{ 0x3110d425,	NULL },	/* INT1031 */
179189499Srnoland	{ 0x3010d425,	NULL },	/* INT1030 */
180189499Srnoland	{ 0,		NULL },
181189499Srnoland};
182189499Srnoland
183189499Srnolandstatic int
184189499Srnolandlook_for_card (u_int iobase)
185189499Srnoland{
186189499Srnoland	int count1, count2;
187189499Srnoland
188189499Srnoland	/*
189189499Srnoland	 * Check for the i82595 signature, and check that the round robin
190189499Srnoland	 * counter actually advances.
191189499Srnoland	 */
192189499Srnoland	if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig)
193189499Srnoland		return(0);
194189499Srnoland	count2 = inb(iobase + ID_REG);
195189909Srnoland	count2 = inb(iobase + ID_REG);
196189499Srnoland	count2 = inb(iobase + ID_REG);
197189499Srnoland
198189499Srnoland	return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits));
199189499Srnoland}
200189499Srnoland
201189499Srnolandstatic int
202189499Srnolandex_get_media (u_int32_t iobase)
203189499Srnoland{
204189499Srnoland	int	tmp;
205189499Srnoland
206189499Srnoland	outb(iobase + CMD_REG, Bank2_Sel);
207189499Srnoland	tmp = inb(iobase + REG3);
208189499Srnoland	outb(iobase + CMD_REG, Bank0_Sel);
209189499Srnoland
210189499Srnoland	if (tmp & TPE_bit)
211189499Srnoland		return(IFM_ETHER|IFM_10_T);
212189499Srnoland	if (tmp & BNC_bit)
213189499Srnoland		return(IFM_ETHER|IFM_10_2);
214189499Srnoland
215189499Srnoland	return (IFM_ETHER|IFM_10_5);
216189499Srnoland}
217189499Srnoland
218189499Srnolandstatic void
219189499Srnolandex_get_address (u_int32_t iobase, u_char *enaddr)
220189499Srnoland{
221189499Srnoland	u_int16_t	eaddr_tmp;
222189499Srnoland
223189499Srnoland	eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo);
224189499Srnoland	enaddr[5] = eaddr_tmp & 0xff;
225189499Srnoland	enaddr[4] = eaddr_tmp >> 8;
226189499Srnoland	eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid);
227189499Srnoland	enaddr[3] = eaddr_tmp & 0xff;
228189499Srnoland	enaddr[2] = eaddr_tmp >> 8;
229189499Srnoland	eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi);
230189499Srnoland	enaddr[1] = eaddr_tmp & 0xff;
231189499Srnoland	enaddr[0] = eaddr_tmp >> 8;
232189499Srnoland
233189499Srnoland	return;
234189499Srnoland}
235189499Srnoland
236189499Srnolandstatic int
237189499Srnolandex_card_type (u_char *enaddr)
238189499Srnoland{
239189499Srnoland	if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9))
240189499Srnoland		return (CARD_TYPE_EX_10_PLUS);
241189499Srnoland
242189499Srnoland	return (CARD_TYPE_EX_10);
243189499Srnoland}
244189499Srnoland
245189499Srnoland/*
246189499Srnoland * Non-destructive identify.
247189499Srnoland */
248189499Srnolandstatic void
249189499Srnolandex_isa_identify (driver_t *driver, device_t parent)
250189499Srnoland{
251189499Srnoland	device_t	child;
252189499Srnoland	u_int32_t	ioport;
253189499Srnoland	u_char 		enaddr[6];
254189499Srnoland	u_int		irq;
255189499Srnoland	int		tmp;
256189499Srnoland	const char *	desc;
257189499Srnoland
258189499Srnoland	if (bootverbose)
259189499Srnoland		printf("ex_isa_identify()\n");
260189499Srnoland
261189499Srnoland	for (ioport = 0x200; ioport < 0x3a0; ioport += 0x10) {
262189499Srnoland
263189499Srnoland		/* No board found at address */
264189499Srnoland		if (!look_for_card(ioport)) {
265189499Srnoland			continue;
266189499Srnoland		}
267189499Srnoland
268189499Srnoland		if (bootverbose)
269189499Srnoland			printf("ex: Found card at 0x%03x!\n", ioport);
270189499Srnoland
271189499Srnoland		/* Board in PnP mode */
272189499Srnoland		if (eeprom_read(ioport, EE_W0) & EE_W0_PNP) {
273189499Srnoland			/* Reset the card. */
274189499Srnoland			outb(ioport + CMD_REG, Reset_CMD);
275189499Srnoland			DELAY(500);
276189499Srnoland			if (bootverbose)
277189499Srnoland				printf("ex: card at 0x%03x in PnP mode!\n", ioport);
278189499Srnoland			continue;
279189499Srnoland		}
280189499Srnoland
281189499Srnoland		bzero(enaddr, sizeof(enaddr));
282189499Srnoland
283189499Srnoland		/* Reset the card. */
284189499Srnoland		outb(ioport + CMD_REG, Reset_CMD);
285190595Srnoland		DELAY(400);
286190595Srnoland
287189499Srnoland		ex_get_address(ioport, enaddr);
288189499Srnoland		tmp = eeprom_read(ioport, EE_W1) & EE_W1_INT_SEL;
289190595Srnoland
290190595Srnoland		/* work out which set of irq <-> internal tables to use */
291190595Srnoland		if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) {
292190595Srnoland			irq  = plus_ee2irqmap[tmp];
293190595Srnoland			desc = "Intel Pro/10+";
294190595Srnoland		} else {
295190595Srnoland			irq = ee2irqmap[tmp];
296190595Srnoland			desc = "Intel Pro/10";
297190595Srnoland		}
298190595Srnoland
299190595Srnoland		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ex", -1);
300190595Srnoland		device_set_desc_copy(child, desc);
301190595Srnoland		device_set_driver(child, driver);
302190595Srnoland		bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
303190595Srnoland		bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EX_IOSIZE);
304190595Srnoland
305190595Srnoland		if (bootverbose)
306190595Srnoland			printf("ex: Adding board at 0x%03x, irq %d\n", ioport, irq);
307190595Srnoland	}
308190595Srnoland
309190595Srnoland	return;
310190595Srnoland}
311190595Srnoland
312190595Srnolandstatic int
313190595Srnolandex_isa_probe(device_t dev)
314190595Srnoland{
315190595Srnoland	u_int		iobase;
316190595Srnoland	u_int		irq;
317190595Srnoland	char *		irq2ee;
318190595Srnoland	u_char *	ee2irq;
319190595Srnoland	u_char 		enaddr[6];
320190595Srnoland	int		tmp;
321190595Srnoland	int		error;
322190595Srnoland
323190595Srnoland	DODEBUG(Start_End, printf("ex_probe: start\n"););
324190595Srnoland
325190595Srnoland	/* Check isapnp ids */
326190674Srnoland	error = ISA_PNP_PROBE(device_get_parent(dev), dev, ex_ids);
327190595Srnoland
328189499Srnoland	/* If the card had a PnP ID that didn't match any we know about */
329190674Srnoland	if (error == ENXIO) {
330190674Srnoland		return(error);
331190674Srnoland	}
332190674Srnoland
333190674Srnoland	/* If we had some other problem. */
334190674Srnoland	if (!(error == 0 || error == ENOENT)) {
335190674Srnoland		return(error);
336190674Srnoland	}
337190674Srnoland
338190674Srnoland	iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0);
339190674Srnoland	if (!iobase) {
340190674Srnoland		printf("ex: no iobase?\n");
341190674Srnoland		return(ENXIO);
342190674Srnoland	}
343190674Srnoland
344190595Srnoland	if (!look_for_card(iobase)) {
345190595Srnoland		printf("ex: no card found at 0x%03x\n", iobase);
346190595Srnoland		return(ENXIO);
347190595Srnoland	}
348189499Srnoland
349190595Srnoland	if (bootverbose)
350190674Srnoland		printf("ex: ex_isa_probe() found card at 0x%03x\n", iobase);
351190595Srnoland
352189499Srnoland	/*
353189499Srnoland	 * Reset the card.
354189499Srnoland	 */
355189499Srnoland	outb(iobase + CMD_REG, Reset_CMD);
356189499Srnoland	DELAY(800);
357189499Srnoland
358189499Srnoland	ex_get_address(iobase, enaddr);
359189499Srnoland
360189499Srnoland	/* work out which set of irq <-> internal tables to use */
361189499Srnoland	if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) {
362189499Srnoland		irq2ee = plus_irq2eemap;
363189499Srnoland		ee2irq = plus_ee2irqmap;
364189499Srnoland	} else {
365189499Srnoland		irq2ee = irq2eemap;
366189499Srnoland		ee2irq = ee2irqmap;
367189499Srnoland	}
368189499Srnoland
369189499Srnoland	tmp = eeprom_read(iobase, EE_W1) & EE_W1_INT_SEL;
370189499Srnoland	irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0);
371189499Srnoland
372189499Srnoland	if (irq > 0) {
373189499Srnoland		/* This will happen if board is in PnP mode. */
374189499Srnoland		if (ee2irq[tmp] != irq) {
375189499Srnoland			printf("ex: WARNING: board's EEPROM is configured"
376189499Srnoland				" for IRQ %d, using %d\n",
377189499Srnoland				ee2irq[tmp], irq);
378189499Srnoland		}
379189499Srnoland	} else {
380189499Srnoland		irq = ee2irq[tmp];
381189499Srnoland		bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
382189499Srnoland	}
383189499Srnoland
384189499Srnoland	if (irq == 0) {
385189499Srnoland		printf("ex: invalid IRQ.\n");
386189499Srnoland		return(ENXIO);
387189499Srnoland	}
388189499Srnoland
389189499Srnoland	DODEBUG(Start_End, printf("ex_probe: finish\n"););
390189499Srnoland
391189499Srnoland	return(0);
392189499Srnoland}
393189499Srnoland
394189499Srnolandstatic int
395189499Srnolandex_isa_attach(device_t dev)
396189499Srnoland{
397189499Srnoland	struct ex_softc *	sc = device_get_softc(dev);
398189499Srnoland	struct ifnet *		ifp = &sc->arpcom.ac_if;
399189499Srnoland	struct ifmedia *	ifm;
400189499Srnoland	int			unit = device_get_unit(dev);
401189499Srnoland	int			error;
402189499Srnoland	int			rid;
403189499Srnoland	void *			ih;
404189499Srnoland	u_int16_t		temp;
405189499Srnoland
406189499Srnoland	DODEBUG(Start_End, device_printf(dev, "start\n"););
407189499Srnoland
408189499Srnoland	rid = 0;
409189499Srnoland	sc->ioport  = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
410189499Srnoland					 0, ~0, 1, RF_ACTIVE);
411189499Srnoland
412189499Srnoland	if (!sc->ioport) {
413190595Srnoland		device_printf(dev, "No I/O space?!\n");
414190595Srnoland		goto bad;
415189499Srnoland	}
416189499Srnoland
417190595Srnoland	rid = 0;
418190595Srnoland	sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
419190674Srnoland				     0, ~0, 1, RF_ACTIVE);
420190595Srnoland
421190595Srnoland	if (!sc->irq) {
422190595Srnoland		device_printf(dev, "No IRQ?!\n");
423190595Srnoland		goto bad;
424195501Srnoland	}
425195501Srnoland
426190595Srnoland	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
427190595Srnoland			       ex_intr, (void *)sc, &ih);
428190595Srnoland
429190595Srnoland	if (error) {
430190595Srnoland		device_printf(dev, "bus_setup_intr() failed!\n");
431190595Srnoland		goto bad;
432190595Srnoland	}
433190595Srnoland
434190595Srnoland	/*
435190674Srnoland	 * Fill in several fields of the softc structure:
436190595Srnoland	 *	- I/O base address.
437189499Srnoland	 *	- Hardware Ethernet address.
438190674Srnoland	 *	- IRQ number (if not supplied in config file, read it from EEPROM).
439190674Srnoland	 *	- Connector type.
440190674Srnoland	 */
441190674Srnoland	sc->dev = dev;
442190674Srnoland	sc->iobase = rman_get_start(sc->ioport);
443190674Srnoland	sc->irq_no = rman_get_start(sc->irq);
444190674Srnoland
445190674Srnoland	ex_get_address(sc->iobase, sc->arpcom.ac_enaddr);
446190674Srnoland
447190674Srnoland	/* work out which set of irq <-> internal tables to use */
448190674Srnoland	if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) {
449190674Srnoland		sc->irq2ee = plus_irq2eemap;
450190595Srnoland		sc->ee2irq = plus_ee2irqmap;
451190674Srnoland	} else {
452190595Srnoland		sc->irq2ee = irq2eemap;
453190595Srnoland		sc->ee2irq = ee2irqmap;
454189499Srnoland	}
455190595Srnoland
456190674Srnoland	sc->mem_size = CARD_RAM_SIZE;	/* XXX This should be read from the card itself. */
457190595Srnoland
458190595Srnoland	/*
459189499Srnoland	 * Initialize the ifnet structure.
460189499Srnoland	 */
461189499Srnoland	ifp->if_softc = sc;
462189499Srnoland	ifp->if_unit = unit;
463189499Srnoland	ifp->if_name = "ex";
464189499Srnoland	ifp->if_mtu = ETHERMTU;
465189499Srnoland	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST /* XXX not done yet. | IFF_MULTICAST */;
466189499Srnoland	ifp->if_output = ether_output;
467189499Srnoland	ifp->if_start = ex_start;
468189499Srnoland	ifp->if_ioctl = ex_ioctl;
469189499Srnoland	ifp->if_watchdog = ex_watchdog;
470189499Srnoland	ifp->if_init = ex_init;
471189499Srnoland	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
472189499Srnoland
473189499Srnoland	ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts);
474189499Srnoland
475189499Srnoland	temp = eeprom_read(sc->iobase, EE_W5);
476189499Srnoland	if (temp & EE_W5_PORT_TPE)
477189499Srnoland		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
478189499Srnoland	if (temp & EE_W5_PORT_BNC)
479189499Srnoland		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL);
480189499Srnoland	if (temp & EE_W5_PORT_AUI)
481189499Srnoland		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL);
482189499Srnoland
483189499Srnoland	ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase));
484189499Srnoland
485189499Srnoland	ifm = &sc->ifmedia;
486189499Srnoland	ifm->ifm_media = ifm->ifm_cur->ifm_media;
487189499Srnoland	ex_ifmedia_upd(ifp);
488189499Srnoland
489189499Srnoland	/*
490189499Srnoland	 * Attach the interface.
491189499Srnoland	 */
492189499Srnoland	if_attach(ifp);
493189499Srnoland	ether_ifattach(ifp);
494189499Srnoland
495189499Srnoland	temp = eeprom_read(sc->iobase, EE_W0);
496189499Srnoland	device_printf(sc->dev, "%s config, %s bus, ",
497189499Srnoland		(temp & EE_W0_PNP) ? "PnP" : "Manual",
498189499Srnoland		(temp & EE_W0_BUS16) ? "16-bit" : "8-bit");
499189499Srnoland
500189499Srnoland	temp = eeprom_read(sc->iobase, EE_W6);
501189499Srnoland	printf("board id 0x%03x, stepping 0x%01x\n",
502189499Srnoland			(temp & EE_W6_BOARD_MASK) >> EE_W6_BOARD_SHIFT,
503189499Srnoland			temp & EE_W6_STEP_MASK);
504189499Srnoland
505189499Srnoland	device_printf(sc->dev, "Ethernet address %6D\n",
506189499Srnoland			sc->arpcom.ac_enaddr, ":");
507189499Srnoland	/*
508189499Srnoland	 * If BPF is in the kernel, call the attach for it
509189499Srnoland	 */
510189499Srnoland	bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
511189499Srnoland	DODEBUG(Start_End, printf("ex_isa_attach%d: finish\n", unit););
512189499Srnoland
513189499Srnoland	return(0);
514189499Srnolandbad:
515189499Srnoland
516189499Srnoland	if (sc->ioport)
517189499Srnoland		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->ioport);
518189499Srnoland	if (sc->irq)
519189499Srnoland		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
520189499Srnoland
521189499Srnoland	return (-1);
522189499Srnoland}
523189499Srnoland
524189499Srnoland
525189499Srnolandstatic void
526189499Srnolandex_init(void *xsc)
527189499Srnoland{
528189499Srnoland	struct ex_softc *	sc = (struct ex_softc *) xsc;
529189499Srnoland	struct ifnet *		ifp = &sc->arpcom.ac_if;
530189499Srnoland	int			s;
531189499Srnoland	int			i;
532189499Srnoland	register int		iobase = sc->iobase;
533189499Srnoland	unsigned short		temp_reg;
534189499Srnoland
535189499Srnoland	DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit););
536189499Srnoland
537189499Srnoland	if (ifp->if_addrhead.tqh_first == NULL) {
538189499Srnoland		return;
539189499Srnoland	}
540189499Srnoland	s = splimp();
541189499Srnoland	sc->arpcom.ac_if.if_timer = 0;
542189499Srnoland
543189499Srnoland	/*
544189499Srnoland	 * Load the ethernet address into the card.
545189499Srnoland	 */
546189499Srnoland	outb(iobase + CMD_REG, Bank2_Sel);
547189499Srnoland	temp_reg = inb(iobase + EEPROM_REG);
548189499Srnoland	if (temp_reg & Trnoff_Enable) {
549189499Srnoland		outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable);
550189499Srnoland	}
551189499Srnoland	for (i = 0; i < ETHER_ADDR_LEN; i++) {
552189499Srnoland		outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]);
553189499Srnoland	}
554189499Srnoland	/*
555189499Srnoland	 * - Setup transmit chaining and discard bad received frames.
556189499Srnoland	 * - Match broadcast.
557189499Srnoland	 * - Clear test mode.
558189499Srnoland	 * - Set receiving mode.
559189499Srnoland	 * - Set IRQ number.
560189499Srnoland	 */
561189499Srnoland	outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr);
562189499Srnoland	outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem);
563189499Srnoland	outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ );
564189499Srnoland	outb(iobase + CMD_REG, Bank1_Sel);
565189499Srnoland	outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]);
566189499Srnoland
567189499Srnoland	/*
568189499Srnoland	 * Divide the available memory in the card into rcv and xmt buffers.
569189499Srnoland	 * By default, I use the first 3/4 of the memory for the rcv buffer,
570189499Srnoland	 * and the remaining 1/4 of the memory for the xmt buffer.
571189499Srnoland	 */
572189499Srnoland	sc->rx_mem_size = sc->mem_size * 3 / 4;
573189499Srnoland	sc->tx_mem_size = sc->mem_size - sc->rx_mem_size;
574189499Srnoland	sc->rx_lower_limit = 0x0000;
575189499Srnoland	sc->rx_upper_limit = sc->rx_mem_size - 2;
576189499Srnoland	sc->tx_lower_limit = sc->rx_mem_size;
577189499Srnoland	sc->tx_upper_limit = sc->mem_size - 2;
578189499Srnoland	outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8);
579189499Srnoland	outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8);
580189499Srnoland	outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8);
581189499Srnoland	outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8);
582189499Srnoland
583189499Srnoland	/*
584189499Srnoland	 * Enable receive and transmit interrupts, and clear any pending int.
585189499Srnoland	 */
586189499Srnoland	outb(iobase + REG1, inb(iobase + REG1) | TriST_INT);
587189499Srnoland	outb(iobase + CMD_REG, Bank0_Sel);
588189499Srnoland	outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int));
589189499Srnoland	outb(iobase + STATUS_REG, All_Int);
590189499Srnoland
591189499Srnoland	/*
592189499Srnoland	 * Initialize receive and transmit ring buffers.
593189499Srnoland	 */
594189499Srnoland	outw(iobase + RCV_BAR, sc->rx_lower_limit);
595189499Srnoland	sc->rx_head = sc->rx_lower_limit;
596189499Srnoland	outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe);
597189499Srnoland	outw(iobase + XMT_BAR, sc->tx_lower_limit);
598189499Srnoland	sc->tx_head = sc->tx_tail = sc->tx_lower_limit;
599189499Srnoland
600189499Srnoland	ifp->if_flags |= IFF_RUNNING;
601189499Srnoland	ifp->if_flags &= ~IFF_OACTIVE;
602189499Srnoland	DODEBUG(Status, printf("OIDLE init\n"););
603189499Srnoland
604189499Srnoland	/*
605189499Srnoland	 * Final reset of the board, and enable operation.
606189499Srnoland	 */
607189499Srnoland	outb(iobase + CMD_REG, Sel_Reset_CMD);
608189499Srnoland	DELAY(2);
609189499Srnoland	outb(iobase + CMD_REG, Rcv_Enable_CMD);
610189499Srnoland
611189499Srnoland	ex_start(ifp);
612189499Srnoland	splx(s);
613189499Srnoland
614189499Srnoland	DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit););
615189499Srnoland}
616189499Srnoland
617189499Srnoland
618189499Srnolandstatic void
619189499Srnolandex_start(struct ifnet *ifp)
620189499Srnoland{
621189499Srnoland	struct ex_softc *	sc = ifp->if_softc;
622189499Srnoland	int			iobase = sc->iobase;
623189499Srnoland	int			i, s, len, data_len, avail, dest, next;
624189499Srnoland	unsigned char		tmp16[2];
625189499Srnoland	struct mbuf *		opkt;
626189499Srnoland	struct mbuf *		m;
627189499Srnoland
628189499Srnoland	DODEBUG(Start_End, printf("ex_start%d: start\n", unit););
629189499Srnoland
630189499Srnoland	s = splimp();
631189499Srnoland
632189499Srnoland	/*
633189499Srnoland	 * Main loop: send outgoing packets to network card until there are no
634189499Srnoland	 * more packets left, or the card cannot accept any more yet.
635189499Srnoland	 */
636189499Srnoland	while (((opkt = ifp->if_snd.ifq_head) != NULL) &&
637189499Srnoland	       !(ifp->if_flags & IFF_OACTIVE)) {
638189499Srnoland
639189499Srnoland		/*
640189499Srnoland		 * Ensure there is enough free transmit buffer space for
641189499Srnoland		 * this packet, including its header. Note: the header
642189499Srnoland		 * cannot wrap around the end of the transmit buffer and
643189499Srnoland		 * must be kept together, so we allow space for twice the
644189499Srnoland		 * length of the header, just in case.
645189499Srnoland		 */
646189499Srnoland
647189499Srnoland		for (len = 0, m = opkt; m != NULL; m = m->m_next) {
648189499Srnoland			len += m->m_len;
649189499Srnoland		}
650189499Srnoland
651189499Srnoland		data_len = len;
652189499Srnoland
653189499Srnoland		DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len););
654189499Srnoland
655189499Srnoland		if (len & 1) {
656189499Srnoland			len += XMT_HEADER_LEN + 1;
657189499Srnoland		} else {
658189499Srnoland			len += XMT_HEADER_LEN;
659189499Srnoland		}
660189499Srnoland
661189499Srnoland		if ((i = sc->tx_tail - sc->tx_head) >= 0) {
662189499Srnoland			avail = sc->tx_mem_size - i;
663189499Srnoland		} else {
664189499Srnoland			avail = -i;
665189499Srnoland		}
666189499Srnoland
667189499Srnoland		DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail););
668189499Srnoland
669189499Srnoland		if (avail >= len + XMT_HEADER_LEN) {
670189499Srnoland			IF_DEQUEUE(&ifp->if_snd, opkt);
671189499Srnoland
672189499Srnoland#ifdef EX_PSA_INTR
673189499Srnoland			/*
674189499Srnoland			 * Disable rx and tx interrupts, to avoid corruption
675189499Srnoland			 * of the host address register by interrupt service
676189499Srnoland			 * routines.
677189499Srnoland			 * XXX Is this necessary with splimp() enabled?
678189499Srnoland			 */
679189499Srnoland			outb(iobase + MASK_REG, All_Int);
680189499Srnoland#endif
681189499Srnoland
682189499Srnoland			/*
683189499Srnoland			 * Compute the start and end addresses of this
684189499Srnoland			 * frame in the tx buffer.
685189499Srnoland			 */
686189499Srnoland			dest = sc->tx_tail;
687189499Srnoland			next = dest + len;
688189499Srnoland
689189499Srnoland			if (next > sc->tx_upper_limit) {
690189499Srnoland				if ((sc->tx_upper_limit + 2 - sc->tx_tail) <=
691189499Srnoland				    XMT_HEADER_LEN) {
692189499Srnoland					dest = sc->tx_lower_limit;
693189499Srnoland					next = dest + len;
694189499Srnoland				} else {
695189499Srnoland					next = sc->tx_lower_limit +
696189499Srnoland						next - sc->tx_upper_limit - 2;
697189499Srnoland				}
698189499Srnoland			}
699189499Srnoland
700189499Srnoland			/*
701189499Srnoland			 * Build the packet frame in the card's ring buffer.
702189499Srnoland			 */
703189499Srnoland			DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next););
704189499Srnoland
705189499Srnoland			outw(iobase + HOST_ADDR_REG, dest);
706189499Srnoland			outw(iobase + IO_PORT_REG, Transmit_CMD);
707189499Srnoland			outw(iobase + IO_PORT_REG, 0);
708189499Srnoland			outw(iobase + IO_PORT_REG, next);
709189499Srnoland			outw(iobase + IO_PORT_REG, data_len);
710189499Srnoland
711189499Srnoland			/*
712189499Srnoland			 * Output the packet data to the card. Ensure all
713189499Srnoland			 * transfers are 16-bit wide, even if individual
714189499Srnoland			 * mbufs have odd length.
715189499Srnoland			 */
716189499Srnoland
717189499Srnoland			for (m = opkt, i = 0; m != NULL; m = m->m_next) {
718189499Srnoland				DODEBUG(Sent_Pkts, printf("[%d]", m->m_len););
719189499Srnoland				if (i) {
720189499Srnoland					tmp16[1] = *(mtod(m, caddr_t));
721189499Srnoland					outsw(iobase + IO_PORT_REG, tmp16, 1);
722189499Srnoland				}
723189499Srnoland				outsw(iobase + IO_PORT_REG,
724189499Srnoland				      mtod(m, caddr_t) + i, (m->m_len - i) / 2);
725189499Srnoland
726189499Srnoland				if ((i = (m->m_len - i) & 1) != 0) {
727189499Srnoland					tmp16[0] = *(mtod(m, caddr_t) +
728189499Srnoland						   m->m_len - 1);
729189499Srnoland				}
730189499Srnoland			}
731189499Srnoland			if (i) {
732189499Srnoland				outsw(iobase + IO_PORT_REG, tmp16, 1);
733189499Srnoland			}
734189499Srnoland
735189499Srnoland			/*
736189499Srnoland			 * If there were other frames chained, update the
737189499Srnoland			 * chain in the last one.
738189499Srnoland			 */
739189499Srnoland			if (sc->tx_head != sc->tx_tail) {
740189499Srnoland				if (sc->tx_tail != dest) {
741189499Srnoland					outw(iobase + HOST_ADDR_REG,
742189499Srnoland					     sc->tx_last + XMT_Chain_Point);
743189499Srnoland					outw(iobase + IO_PORT_REG, dest);
744189499Srnoland				}
745189499Srnoland				outw(iobase + HOST_ADDR_REG,
746189499Srnoland				     sc->tx_last + XMT_Byte_Count);
747189499Srnoland				i = inw(iobase + IO_PORT_REG);
748189499Srnoland				outw(iobase + HOST_ADDR_REG,
749189499Srnoland				     sc->tx_last + XMT_Byte_Count);
750189499Srnoland				outw(iobase + IO_PORT_REG, i | Ch_bit);
751189499Srnoland			}
752189499Srnoland
753189499Srnoland			/*
754189499Srnoland			 * Resume normal operation of the card:
755189499Srnoland			 * - Make a dummy read to flush the DRAM write
756189499Srnoland			 *   pipeline.
757189499Srnoland			 * - Enable receive and transmit interrupts.
758189499Srnoland			 * - Send Transmit or Resume_XMT command, as
759189499Srnoland			 *   appropriate.
760189499Srnoland			 */
761189499Srnoland			inw(iobase + IO_PORT_REG);
762189499Srnoland#ifdef EX_PSA_INTR
763189499Srnoland			outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int));
764189499Srnoland#endif
765189499Srnoland			if (sc->tx_head == sc->tx_tail) {
766189499Srnoland				outw(iobase + XMT_BAR, dest);
767189499Srnoland				outb(iobase + CMD_REG, Transmit_CMD);
768189499Srnoland				sc->tx_head = dest;
769189499Srnoland				DODEBUG(Sent_Pkts, printf("Transmit\n"););
770189499Srnoland			} else {
771189499Srnoland				outb(iobase + CMD_REG, Resume_XMT_List_CMD);
772189499Srnoland				DODEBUG(Sent_Pkts, printf("Resume\n"););
773189499Srnoland			}
774189499Srnoland
775189499Srnoland			sc->tx_last = dest;
776189499Srnoland			sc->tx_tail = next;
777189499Srnoland
778189499Srnoland			if (ifp->if_bpf != NULL) {
779189499Srnoland				bpf_mtap(ifp, opkt);
780189499Srnoland			}
781189499Srnoland
782189499Srnoland			ifp->if_timer = 2;
783189499Srnoland			ifp->if_opackets++;
784189499Srnoland			m_freem(opkt);
785189499Srnoland		} else {
786189499Srnoland			ifp->if_flags |= IFF_OACTIVE;
787189499Srnoland			DODEBUG(Status, printf("OACTIVE start\n"););
788189499Srnoland		}
789189499Srnoland	}
790189499Srnoland
791189499Srnoland	splx(s);
792189499Srnoland
793189499Srnoland	DODEBUG(Start_End, printf("ex_start%d: finish\n", unit););
794189499Srnoland}
795189499Srnoland
796189499Srnolandstatic void
797189499Srnolandex_stop(struct ex_softc *sc)
798189499Srnoland{
799189499Srnoland	int iobase = sc->iobase;
800189499Srnoland
801189499Srnoland	DODEBUG(Start_End, printf("ex_stop%d: start\n", unit););
802189499Srnoland
803189499Srnoland	/*
804189499Srnoland	 * Disable card operation:
805189499Srnoland	 * - Disable the interrupt line.
806189499Srnoland	 * - Flush transmission and disable reception.
807189499Srnoland	 * - Mask and clear all interrupts.
808189499Srnoland	 * - Reset the 82595.
809189499Srnoland	 */
810189499Srnoland	outb(iobase + CMD_REG, Bank1_Sel);
811189499Srnoland	outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT);
812189499Srnoland	outb(iobase + CMD_REG, Bank0_Sel);
813189499Srnoland	outb(iobase + CMD_REG, Rcv_Stop);
814189499Srnoland	sc->tx_head = sc->tx_tail = sc->tx_lower_limit;
815189499Srnoland	sc->tx_last = 0; /* XXX I think these two lines are not necessary, because ex_init will always be called again to reinit the interface. */
816189499Srnoland	outb(iobase + MASK_REG, All_Int);
817189499Srnoland	outb(iobase + STATUS_REG, All_Int);
818189499Srnoland	outb(iobase + CMD_REG, Reset_CMD);
819189499Srnoland	DELAY(200);
820189499Srnoland
821189499Srnoland	DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit););
822189499Srnoland
823189499Srnoland	return;
824189499Srnoland}
825189499Srnoland
826189499Srnoland
827189499Srnolandstatic void
828189499Srnolandex_intr(void *arg)
829189499Srnoland{
830189499Srnoland	struct ex_softc *	sc = (struct ex_softc *)arg;
831189499Srnoland	struct ifnet *	ifp = &sc->arpcom.ac_if;
832189499Srnoland	int			iobase = sc->iobase;
833189499Srnoland	int			int_status, send_pkts;
834189499Srnoland
835189499Srnoland	DODEBUG(Start_End, printf("ex_intr%d: start\n", unit););
836189499Srnoland
837189499Srnoland#ifdef EXDEBUG
838189499Srnoland	if (++exintr_count != 1)
839189499Srnoland		printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count);
840189499Srnoland#endif
841189499Srnoland
842189499Srnoland	send_pkts = 0;
843189499Srnoland	while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) {
844189499Srnoland		if (int_status & Rx_Int) {
845189499Srnoland			outb(iobase + STATUS_REG, Rx_Int);
846189499Srnoland
847189499Srnoland			ex_rx_intr(sc);
848189499Srnoland		} else if (int_status & Tx_Int) {
849189499Srnoland			outb(iobase + STATUS_REG, Tx_Int);
850189499Srnoland
851189499Srnoland			ex_tx_intr(sc);
852189499Srnoland			send_pkts = 1;
853189499Srnoland		}
854189499Srnoland	}
855189499Srnoland
856189499Srnoland	/*
857189499Srnoland	 * If any packet has been transmitted, and there are queued packets to
858189499Srnoland	 * be sent, attempt to send more packets to the network card.
859189499Srnoland	 */
860189499Srnoland
861189499Srnoland	if (send_pkts && (ifp->if_snd.ifq_head != NULL)) {
862189499Srnoland		ex_start(ifp);
863189499Srnoland	}
864189499Srnoland
865189499Srnoland#ifdef EXDEBUG
866189499Srnoland	exintr_count--;
867189499Srnoland#endif
868189499Srnoland
869189499Srnoland	DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit););
870189499Srnoland
871189499Srnoland	return;
872189499Srnoland}
873189499Srnoland
874189499Srnolandstatic void
875189499Srnolandex_tx_intr(struct ex_softc *sc)
876189499Srnoland{
877189499Srnoland	struct ifnet *	ifp = &sc->arpcom.ac_if;
878189499Srnoland	int		iobase = sc->iobase;
879189499Srnoland	int		tx_status;
880189499Srnoland
881189499Srnoland	DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit););
882189499Srnoland
883189499Srnoland	/*
884189499Srnoland	 * - Cancel the watchdog.
885189499Srnoland	 * For all packets transmitted since last transmit interrupt:
886189499Srnoland	 * - Advance chain pointer to next queued packet.
887189499Srnoland	 * - Update statistics.
888189499Srnoland	 */
889189499Srnoland
890189499Srnoland	ifp->if_timer = 0;
891189499Srnoland
892189499Srnoland	while (sc->tx_head != sc->tx_tail) {
893189499Srnoland		outw(iobase + HOST_ADDR_REG, sc->tx_head);
894189499Srnoland
895189499Srnoland		if (! inw(iobase + IO_PORT_REG) & Done_bit)
896189499Srnoland			break;
897189499Srnoland
898189499Srnoland		tx_status = inw(iobase + IO_PORT_REG);
899189499Srnoland		sc->tx_head = inw(iobase + IO_PORT_REG);
900189499Srnoland
901189499Srnoland		if (tx_status & TX_OK_bit) {
902189499Srnoland			ifp->if_opackets++;
903189499Srnoland		} else {
904189499Srnoland			ifp->if_oerrors++;
905189499Srnoland		}
906189499Srnoland
907189499Srnoland		ifp->if_collisions += tx_status & No_Collisions_bits;
908189499Srnoland	}
909189499Srnoland
910189499Srnoland	/*
911189499Srnoland	 * The card should be ready to accept more packets now.
912189499Srnoland	 */
913189499Srnoland
914189499Srnoland	ifp->if_flags &= ~IFF_OACTIVE;
915189499Srnoland
916189499Srnoland	DODEBUG(Status, printf("OIDLE tx_intr\n"););
917189499Srnoland	DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit););
918189499Srnoland
919189499Srnoland	return;
920189499Srnoland}
921189499Srnoland
922189499Srnolandstatic void
923189499Srnolandex_rx_intr(struct ex_softc *sc)
924189499Srnoland{
925189499Srnoland	struct ifnet *		ifp = &sc->arpcom.ac_if;
926189499Srnoland	int			iobase = sc->iobase;
927189499Srnoland	int			rx_status;
928189499Srnoland	int			pkt_len;
929189499Srnoland	int			QQQ;
930189499Srnoland	struct mbuf *		m;
931189499Srnoland	struct mbuf *		ipkt;
932189499Srnoland	struct ether_header *	eh;
933189499Srnoland
934189499Srnoland	DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit););
935189499Srnoland
936189499Srnoland	/*
937189499Srnoland	 * For all packets received since last receive interrupt:
938189499Srnoland	 * - If packet ok, read it into a new mbuf and queue it to interface,
939189499Srnoland	 *   updating statistics.
940189499Srnoland	 * - If packet bad, just discard it, and update statistics.
941189499Srnoland	 * Finally, advance receive stop limit in card's memory to new location.
942189499Srnoland	 */
943189499Srnoland
944189499Srnoland	outw(iobase + HOST_ADDR_REG, sc->rx_head);
945189499Srnoland
946189499Srnoland	while (inw(iobase + IO_PORT_REG) == RCV_Done) {
947189499Srnoland
948189499Srnoland		rx_status = inw(iobase + IO_PORT_REG);
949189499Srnoland		sc->rx_head = inw(iobase + IO_PORT_REG);
950189499Srnoland		QQQ = pkt_len = inw(iobase + IO_PORT_REG);
951189499Srnoland
952189499Srnoland		if (rx_status & RCV_OK_bit) {
953189499Srnoland			MGETHDR(m, M_DONTWAIT, MT_DATA);
954189499Srnoland			ipkt = m;
955189499Srnoland			if (ipkt == NULL) {
956189499Srnoland				ifp->if_iqdrops++;
957189499Srnoland			} else {
958189499Srnoland				ipkt->m_pkthdr.rcvif = ifp;
959189499Srnoland				ipkt->m_pkthdr.len = pkt_len;
960189499Srnoland				ipkt->m_len = MHLEN;
961189499Srnoland
962189499Srnoland				while (pkt_len > 0) {
963189499Srnoland					if (pkt_len > MINCLSIZE) {
964189499Srnoland						MCLGET(m, M_DONTWAIT);
965189499Srnoland						if (m->m_flags & M_EXT) {
966189499Srnoland							m->m_len = MCLBYTES;
967189499Srnoland						} else {
968189499Srnoland							m_freem(ipkt);
969189499Srnoland							ifp->if_iqdrops++;
970189499Srnoland							goto rx_another;
971189499Srnoland						}
972189499Srnoland					}
973189499Srnoland					m->m_len = min(m->m_len, pkt_len);
974189499Srnoland
975189499Srnoland	  /*
976189499Srnoland	   * NOTE: I'm assuming that all mbufs allocated are of even length,
977189499Srnoland	   * except for the last one in an odd-length packet.
978189499Srnoland	   */
979189499Srnoland
980189499Srnoland					insw(iobase + IO_PORT_REG,
981189499Srnoland					     mtod(m, caddr_t), m->m_len / 2);
982189499Srnoland
983189499Srnoland					if (m->m_len & 1) {
984189499Srnoland						*(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG);
985189499Srnoland					}
986189499Srnoland					pkt_len -= m->m_len;
987189499Srnoland
988189499Srnoland					if (pkt_len > 0) {
989189499Srnoland						MGET(m->m_next, M_DONTWAIT, MT_DATA);
990189499Srnoland						if (m->m_next == NULL) {
991189499Srnoland							m_freem(ipkt);
992189499Srnoland							ifp->if_iqdrops++;
993189499Srnoland							goto rx_another;
994189499Srnoland						}
995189499Srnoland						m = m->m_next;
996189499Srnoland						m->m_len = MLEN;
997189499Srnoland					}
998189499Srnoland				}
999189499Srnoland				eh = mtod(ipkt, struct ether_header *);
1000189499Srnoland#ifdef EXDEBUG
1001189499Srnoland	if (debug_mask & Rcvd_Pkts) {
1002189499Srnoland		if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) {
1003189499Srnoland			printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":");
1004189499Srnoland			printf("%6D\n", eh->ether_dhost, ":");
1005189499Srnoland		} /* QQQ */
1006189499Srnoland	}
1007189499Srnoland#endif
1008189499Srnoland				if (ifp->if_bpf != NULL) {
1009189499Srnoland					bpf_mtap(ifp, ipkt);
1010189499Srnoland
1011189499Srnoland		/*
1012189499Srnoland		 * Note that the interface cannot be in promiscuous mode
1013189499Srnoland		 * if there are no BPF listeners. And if we are in
1014189499Srnoland		 * promiscuous mode, we have to check if this packet is
1015189499Srnoland		 * really ours.
1016189499Srnoland		 */
1017189499Srnoland					if ((ifp->if_flags & IFF_PROMISC) &&
1018189499Srnoland					    (eh->ether_dhost[0] & 1) == 0 &&
1019189499Srnoland					    bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 &&
1020189499Srnoland					    bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) {
1021189499Srnoland						m_freem(ipkt);
1022189499Srnoland						goto rx_another;
1023189499Srnoland					}
1024189499Srnoland				}
1025189499Srnoland				m_adj(ipkt, sizeof(struct ether_header));
1026189499Srnoland				ether_input(ifp, eh, ipkt);
1027189499Srnoland				ifp->if_ipackets++;
1028189499Srnoland			}
1029189499Srnoland		} else {
1030189499Srnoland			ifp->if_ierrors++;
1031189499Srnoland		}
1032189499Srnoland		outw(iobase + HOST_ADDR_REG, sc->rx_head);
1033189499Srnolandrx_another: ;
1034189499Srnoland	}
1035189499Srnoland
1036189499Srnoland	if (sc->rx_head < sc->rx_lower_limit + 2)
1037189499Srnoland		outw(iobase + RCV_STOP_REG, sc->rx_upper_limit);
1038189499Srnoland	else
1039189499Srnoland		outw(iobase + RCV_STOP_REG, sc->rx_head - 2);
1040189499Srnoland
1041189499Srnoland	DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit););
1042189499Srnoland
1043189499Srnoland	return;
1044189499Srnoland}
1045189499Srnoland
1046189499Srnoland
1047189499Srnolandstatic int
1048189499Srnolandex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data)
1049189499Srnoland{
1050189499Srnoland	struct ex_softc *	sc = ifp->if_softc;
1051189499Srnoland	struct ifreq *		ifr = (struct ifreq *)data;
1052189499Srnoland	int			s;
1053189499Srnoland	int			error = 0;
1054189499Srnoland
1055189499Srnoland	DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit););
1056189499Srnoland
1057189499Srnoland	s = splimp();
1058189499Srnoland
1059189499Srnoland	switch(cmd) {
1060189499Srnoland		case SIOCSIFADDR:
1061189499Srnoland		case SIOCGIFADDR:
1062189499Srnoland		case SIOCSIFMTU:
1063189499Srnoland			error = ether_ioctl(ifp, cmd, data);
1064189499Srnoland			break;
1065189499Srnoland
1066189499Srnoland		case SIOCSIFFLAGS:
1067189499Srnoland			DODEBUG(Start_End, printf("SIOCSIFFLAGS"););
1068189499Srnoland			if ((ifp->if_flags & IFF_UP) == 0 &&
1069189499Srnoland			    (ifp->if_flags & IFF_RUNNING)) {
1070189499Srnoland
1071189499Srnoland				ifp->if_flags &= ~IFF_RUNNING;
1072189499Srnoland				ex_stop(sc);
1073189499Srnoland			} else {
1074189499Srnoland      				ex_init(sc);
1075189499Srnoland			}
1076189499Srnoland			break;
1077189499Srnoland#ifdef NODEF
1078189499Srnoland		case SIOCGHWADDR:
1079189499Srnoland			DODEBUG(Start_End, printf("SIOCGHWADDR"););
1080189499Srnoland			bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data,
1081189499Srnoland			      sizeof(sc->sc_addr));
1082189499Srnoland			break;
1083189499Srnoland#endif
1084189499Srnoland		case SIOCADDMULTI:
1085189499Srnoland			DODEBUG(Start_End, printf("SIOCADDMULTI"););
1086189499Srnoland		case SIOCDELMULTI:
1087189499Srnoland			DODEBUG(Start_End, printf("SIOCDELMULTI"););
1088189499Srnoland			/* XXX Support not done yet. */
1089189499Srnoland			error = EINVAL;
1090189499Srnoland			break;
1091189499Srnoland		case SIOCSIFMEDIA:
1092189499Srnoland		case SIOCGIFMEDIA:
1093189499Srnoland			error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd);
1094189499Srnoland			break;
1095189499Srnoland		default:
1096189499Srnoland			DODEBUG(Start_End, printf("unknown"););
1097189499Srnoland			error = EINVAL;
1098189499Srnoland	}
1099189499Srnoland
1100189499Srnoland	splx(s);
1101189499Srnoland
1102189499Srnoland	DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit););
1103189499Srnoland
1104189499Srnoland	return(error);
1105189499Srnoland}
1106189499Srnoland
1107189499Srnoland
1108189499Srnolandstatic void
1109189499Srnolandex_reset(struct ex_softc *sc)
1110189499Srnoland{
1111189499Srnoland	int s;
1112189499Srnoland
1113189499Srnoland	DODEBUG(Start_End, printf("ex_reset%d: start\n", unit););
1114189499Srnoland
1115189499Srnoland	s = splimp();
1116189499Srnoland
1117189499Srnoland	ex_stop(sc);
1118189499Srnoland	ex_init(sc);
1119189499Srnoland
1120189499Srnoland	splx(s);
1121189499Srnoland
1122189499Srnoland	DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit););
1123189499Srnoland
1124189499Srnoland	return;
1125189499Srnoland}
1126189499Srnoland
1127189499Srnolandstatic void
1128189499Srnolandex_watchdog(struct ifnet *ifp)
1129189499Srnoland{
1130189499Srnoland	struct ex_softc *	sc = ifp->if_softc;
1131189499Srnoland
1132189499Srnoland	DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit););
1133189499Srnoland
1134189499Srnoland	ifp->if_flags &= ~IFF_OACTIVE;
1135189499Srnoland
1136189499Srnoland	DODEBUG(Status, printf("OIDLE watchdog\n"););
1137189499Srnoland
1138189499Srnoland	ifp->if_oerrors++;
1139189499Srnoland	ex_reset(sc);
1140189499Srnoland	ex_start(ifp);
1141189499Srnoland
1142189499Srnoland	DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit););
1143189499Srnoland
1144189499Srnoland	return;
1145189499Srnoland}
1146189499Srnoland
1147189499Srnolandstatic int
1148189499Srnolandex_ifmedia_upd (ifp)
1149189499Srnoland	struct ifnet *		ifp;
1150189499Srnoland{
1151189499Srnoland	struct ex_softc *	sc = ifp->if_softc;
1152189499Srnoland
1153189499Srnoland	return (0);
1154189499Srnoland}
1155189499Srnoland
1156189499Srnolandstatic void
1157189499Srnolandex_ifmedia_sts(ifp, ifmr)
1158189499Srnoland	struct ifnet *          ifp;
1159189499Srnoland	struct ifmediareq *     ifmr;
1160189499Srnoland{
1161189499Srnoland	struct ex_softc *       sc = ifp->if_softc;
1162189499Srnoland
1163189499Srnoland	ifmr->ifm_active = ex_get_media(sc->iobase);
1164189499Srnoland
1165189499Srnoland	return;
1166189499Srnoland}
1167189499Srnoland
1168189499Srnolandstatic u_short
1169189499Srnolandeeprom_read(int iobase, int location)
1170189499Srnoland{
1171189499Srnoland	int i;
1172189499Srnoland	u_short data = 0;
1173189499Srnoland	int ee_addr;
1174189499Srnoland	int read_cmd = location | EE_READ_CMD;
1175189499Srnoland	short ctrl_val = EECS;
1176189499Srnoland
1177189499Srnoland	ee_addr = iobase + EEPROM_REG;
1178189499Srnoland	outb(iobase + CMD_REG, Bank2_Sel);
1179189499Srnoland	outb(ee_addr, EECS);
1180189499Srnoland	for (i = 8; i >= 0; i--) {
1181189499Srnoland		short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val;
1182189499Srnoland		outb(ee_addr, outval);
1183189499Srnoland		outb(ee_addr, outval | EESK);
1184189499Srnoland		DELAY(3);
1185189499Srnoland		outb(ee_addr, outval);
1186189499Srnoland		DELAY(2);
1187189499Srnoland	}
1188189499Srnoland	outb(ee_addr, ctrl_val);
1189189499Srnoland
1190189499Srnoland	for (i = 16; i > 0; i--) {
1191189499Srnoland		outb(ee_addr, ctrl_val | EESK);
1192189499Srnoland		DELAY(3);
1193189499Srnoland		data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
1194189499Srnoland		outb(ee_addr, ctrl_val);
1195189499Srnoland		DELAY(2);
1196189499Srnoland	}
1197189499Srnoland
1198189499Srnoland	ctrl_val &= ~EECS;
1199189499Srnoland	outb(ee_addr, ctrl_val | EESK);
1200189499Srnoland	DELAY(3);
1201189499Srnoland	outb(ee_addr, ctrl_val);
1202189499Srnoland	DELAY(2);
1203189499Srnoland	outb(iobase + CMD_REG, Bank0_Sel);
1204189499Srnoland	return(data);
1205189499Srnoland}
1206189499Srnoland