xref: /openbsd-src/sys/dev/ic/am79900.c (revision 63bcfa73e917a5ed3d06db2c3eab553f722c3b1f)
1*63bcfa73Spatrick /*	$OpenBSD: am79900.c,v 1.8 2020/07/10 13:22:19 patrick Exp $	*/
20ec3b04cSmiod /*	$NetBSD: am79900.c,v 1.23 2012/02/02 19:43:02 tls Exp $	*/
30ec3b04cSmiod 
40ec3b04cSmiod /*-
50ec3b04cSmiod  * Copyright (c) 1997 The NetBSD Foundation, Inc.
60ec3b04cSmiod  * All rights reserved.
70ec3b04cSmiod  *
80ec3b04cSmiod  * This code is derived from software contributed to The NetBSD Foundation
90ec3b04cSmiod  * by Jason R. Thorpe.
100ec3b04cSmiod  *
110ec3b04cSmiod  * Redistribution and use in source and binary forms, with or without
120ec3b04cSmiod  * modification, are permitted provided that the following conditions
130ec3b04cSmiod  * are met:
140ec3b04cSmiod  * 1. Redistributions of source code must retain the above copyright
150ec3b04cSmiod  *    notice, this list of conditions and the following disclaimer.
160ec3b04cSmiod  * 2. Redistributions in binary form must reproduce the above copyright
170ec3b04cSmiod  *    notice, this list of conditions and the following disclaimer in the
180ec3b04cSmiod  *    documentation and/or other materials provided with the distribution.
190ec3b04cSmiod  *
200ec3b04cSmiod  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
210ec3b04cSmiod  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
220ec3b04cSmiod  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
230ec3b04cSmiod  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
240ec3b04cSmiod  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
250ec3b04cSmiod  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
260ec3b04cSmiod  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
270ec3b04cSmiod  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
280ec3b04cSmiod  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
290ec3b04cSmiod  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
300ec3b04cSmiod  * POSSIBILITY OF SUCH DAMAGE.
310ec3b04cSmiod  */
320ec3b04cSmiod 
330ec3b04cSmiod /*-
340ec3b04cSmiod  * Copyright (c) 1992, 1993
350ec3b04cSmiod  *	The Regents of the University of California.  All rights reserved.
360ec3b04cSmiod  *
370ec3b04cSmiod  * This code is derived from software contributed to Berkeley by
380ec3b04cSmiod  * Ralph Campbell and Rick Macklem.
390ec3b04cSmiod  *
400ec3b04cSmiod  * Redistribution and use in source and binary forms, with or without
410ec3b04cSmiod  * modification, are permitted provided that the following conditions
420ec3b04cSmiod  * are met:
430ec3b04cSmiod  * 1. Redistributions of source code must retain the above copyright
440ec3b04cSmiod  *    notice, this list of conditions and the following disclaimer.
450ec3b04cSmiod  * 2. Redistributions in binary form must reproduce the above copyright
460ec3b04cSmiod  *    notice, this list of conditions and the following disclaimer in the
470ec3b04cSmiod  *    documentation and/or other materials provided with the distribution.
480ec3b04cSmiod  * 3. Neither the name of the University nor the names of its contributors
490ec3b04cSmiod  *    may be used to endorse or promote products derived from this software
500ec3b04cSmiod  *    without specific prior written permission.
510ec3b04cSmiod  *
520ec3b04cSmiod  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
530ec3b04cSmiod  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
540ec3b04cSmiod  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
550ec3b04cSmiod  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
560ec3b04cSmiod  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
570ec3b04cSmiod  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
580ec3b04cSmiod  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
590ec3b04cSmiod  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
600ec3b04cSmiod  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
610ec3b04cSmiod  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
620ec3b04cSmiod  * SUCH DAMAGE.
630ec3b04cSmiod  *
640ec3b04cSmiod  *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
650ec3b04cSmiod  */
660ec3b04cSmiod 
670ec3b04cSmiod /*-
680ec3b04cSmiod  * Copyright (c) 1998
690ec3b04cSmiod  *	Matthias Drochner.  All rights reserved.
700ec3b04cSmiod  * Copyright (c) 1995 Charles M. Hannum.  All rights reserved.
710ec3b04cSmiod  *
720ec3b04cSmiod  * This code is derived from software contributed to Berkeley by
730ec3b04cSmiod  * Ralph Campbell and Rick Macklem.
740ec3b04cSmiod  *
750ec3b04cSmiod  * Redistribution and use in source and binary forms, with or without
760ec3b04cSmiod  * modification, are permitted provided that the following conditions
770ec3b04cSmiod  * are met:
780ec3b04cSmiod  * 1. Redistributions of source code must retain the above copyright
790ec3b04cSmiod  *    notice, this list of conditions and the following disclaimer.
800ec3b04cSmiod  * 2. Redistributions in binary form must reproduce the above copyright
810ec3b04cSmiod  *    notice, this list of conditions and the following disclaimer in the
820ec3b04cSmiod  *    documentation and/or other materials provided with the distribution.
830ec3b04cSmiod  * 3. All advertising materials mentioning features or use of this software
840ec3b04cSmiod  *    must display the following acknowledgement:
850ec3b04cSmiod  *	This product includes software developed by the University of
860ec3b04cSmiod  *	California, Berkeley and its contributors.
870ec3b04cSmiod  * 4. Neither the name of the University nor the names of its contributors
880ec3b04cSmiod  *    may be used to endorse or promote products derived from this software
890ec3b04cSmiod  *    without specific prior written permission.
900ec3b04cSmiod  *
910ec3b04cSmiod  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
920ec3b04cSmiod  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
930ec3b04cSmiod  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
940ec3b04cSmiod  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
950ec3b04cSmiod  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
960ec3b04cSmiod  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
970ec3b04cSmiod  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
980ec3b04cSmiod  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
990ec3b04cSmiod  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1000ec3b04cSmiod  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1010ec3b04cSmiod  * SUCH DAMAGE.
1020ec3b04cSmiod  *
1030ec3b04cSmiod  *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
1040ec3b04cSmiod  */
1050ec3b04cSmiod 
1060ec3b04cSmiod #include "bpfilter.h"
1070ec3b04cSmiod 
1080ec3b04cSmiod #include <sys/param.h>
1090ec3b04cSmiod #include <sys/systm.h>
1100ec3b04cSmiod #include <sys/mbuf.h>
1110ec3b04cSmiod #include <sys/syslog.h>
1120ec3b04cSmiod #include <sys/socket.h>
1130ec3b04cSmiod #include <sys/device.h>
1140ec3b04cSmiod #include <sys/malloc.h>
1150ec3b04cSmiod #include <sys/ioctl.h>
1160ec3b04cSmiod #include <sys/errno.h>
1170ec3b04cSmiod 
1180ec3b04cSmiod #include <net/if.h>
1190ec3b04cSmiod #include <net/if_media.h>
1200ec3b04cSmiod 
1210ec3b04cSmiod #include <netinet/in.h>
1220ec3b04cSmiod #include <netinet/if_ether.h>
1230ec3b04cSmiod 
1240ec3b04cSmiod #if NBPFILTER > 0
1250ec3b04cSmiod #include <net/bpf.h>
1260ec3b04cSmiod #endif
1270ec3b04cSmiod 
1280ec3b04cSmiod #include <dev/ic/lancereg.h>
1290ec3b04cSmiod #include <dev/ic/lancevar.h>
1300ec3b04cSmiod #include <dev/ic/am79900reg.h>
1310ec3b04cSmiod #include <dev/ic/am79900var.h>
1320ec3b04cSmiod 
1330ec3b04cSmiod void	am79900_meminit(struct lance_softc *);
1340ec3b04cSmiod void	am79900_start(struct ifnet *);
1350ec3b04cSmiod 
1360ec3b04cSmiod void	am79900_rint(struct lance_softc *);
1370ec3b04cSmiod void	am79900_tint(struct lance_softc *);
1380ec3b04cSmiod 
1390ec3b04cSmiod #ifdef LEDEBUG
1400ec3b04cSmiod void	am79900_recv_print(struct lance_softc *, int);
1410ec3b04cSmiod void	am79900_xmit_print(struct lance_softc *, int);
1420ec3b04cSmiod #endif
1430ec3b04cSmiod 
1440ec3b04cSmiod void
am79900_config(struct am79900_softc * sc)1450ec3b04cSmiod am79900_config(struct am79900_softc *sc)
1460ec3b04cSmiod {
1470ec3b04cSmiod 	int mem, i;
1480ec3b04cSmiod 
1490ec3b04cSmiod 	sc->lsc.sc_meminit = am79900_meminit;
1500ec3b04cSmiod 	sc->lsc.sc_start = am79900_start;
1510ec3b04cSmiod 
1520ec3b04cSmiod 	lance_config(&sc->lsc);
1530ec3b04cSmiod 
1540ec3b04cSmiod 	mem = 0;
1550ec3b04cSmiod 	sc->lsc.sc_initaddr = mem;
1560ec3b04cSmiod 	mem += sizeof(struct leinit);
1570ec3b04cSmiod 	sc->lsc.sc_rmdaddr = mem;
1580ec3b04cSmiod 	mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf;
1590ec3b04cSmiod 	sc->lsc.sc_tmdaddr = mem;
1600ec3b04cSmiod 	mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf;
1610ec3b04cSmiod 	for (i = 0; i < sc->lsc.sc_nrbuf; i++, mem += LEBLEN)
1620ec3b04cSmiod 		sc->lsc.sc_rbufaddr[i] = mem;
1630ec3b04cSmiod 	for (i = 0; i < sc->lsc.sc_ntbuf; i++, mem += LEBLEN)
1640ec3b04cSmiod 		sc->lsc.sc_tbufaddr[i] = mem;
1650ec3b04cSmiod 
1660ec3b04cSmiod 	if (mem > sc->lsc.sc_memsize)
1670ec3b04cSmiod 		panic("%s: memsize", sc->lsc.sc_dev.dv_xname);
1680ec3b04cSmiod }
1690ec3b04cSmiod 
1700ec3b04cSmiod /*
1710ec3b04cSmiod  * Set up the initialization block and the descriptor rings.
1720ec3b04cSmiod  */
1730ec3b04cSmiod void
am79900_meminit(struct lance_softc * sc)1740ec3b04cSmiod am79900_meminit(struct lance_softc *sc)
1750ec3b04cSmiod {
1760ec3b04cSmiod 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
1770ec3b04cSmiod 	u_long a;
1780ec3b04cSmiod 	int bix;
1790ec3b04cSmiod 	struct leinit init;
1800ec3b04cSmiod 	struct lermd rmd;
1810ec3b04cSmiod 	struct letmd tmd;
1820ec3b04cSmiod 	uint8_t *myaddr;
1830ec3b04cSmiod 
1840ec3b04cSmiod 	if (ifp->if_flags & IFF_PROMISC)
1850ec3b04cSmiod 		init.init_mode = LE_MODE_NORMAL | LE_MODE_PROM;
1860ec3b04cSmiod 	else
1870ec3b04cSmiod 		init.init_mode = LE_MODE_NORMAL;
1880ec3b04cSmiod 	if (sc->sc_initmodemedia == 1)
1890ec3b04cSmiod 		init.init_mode |= LE_MODE_PSEL0;
1900ec3b04cSmiod 
1910ec3b04cSmiod 	init.init_mode |= ((ffs(sc->sc_ntbuf) - 1) << 28) |
1920ec3b04cSmiod 	    ((ffs(sc->sc_nrbuf) - 1) << 20);
1930ec3b04cSmiod 
1940ec3b04cSmiod 	/*
1950ec3b04cSmiod 	 * Update our private copy of the Ethernet address.
1960ec3b04cSmiod 	 * We NEED the copy so we can ensure its alignment!
1970ec3b04cSmiod 	 */
1980ec3b04cSmiod 	memcpy(sc->sc_enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
1990ec3b04cSmiod 	myaddr = sc->sc_enaddr;
2000ec3b04cSmiod 
2010ec3b04cSmiod 	init.init_padr[0] = myaddr[0] | (myaddr[1] << 8) |
2020ec3b04cSmiod 	    (myaddr[2] << 16) | (myaddr[3] << 24);
2030ec3b04cSmiod 	init.init_padr[1] = myaddr[4] | (myaddr[5] << 8);
2040ec3b04cSmiod 	lance_setladrf(&sc->sc_arpcom, init.init_ladrf);
2050ec3b04cSmiod 
2060ec3b04cSmiod 	sc->sc_last_rd = 0;
2070ec3b04cSmiod 	sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
2080ec3b04cSmiod 
2090ec3b04cSmiod 	a = sc->sc_addr + LE_RMDADDR(sc, 0);
2100ec3b04cSmiod 	init.init_rdra = a;
2110ec3b04cSmiod 
2120ec3b04cSmiod 	a = sc->sc_addr + LE_TMDADDR(sc, 0);
2130ec3b04cSmiod 	init.init_tdra = a;
2140ec3b04cSmiod 
2150ec3b04cSmiod 	(*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init));
2160ec3b04cSmiod 
2170ec3b04cSmiod 	/*
2180ec3b04cSmiod 	 * Set up receive ring descriptors.
2190ec3b04cSmiod 	 */
2200ec3b04cSmiod 	for (bix = 0; bix < sc->sc_nrbuf; bix++) {
2210ec3b04cSmiod 		a = sc->sc_addr + LE_RBUFADDR(sc, bix);
2220ec3b04cSmiod 		rmd.rmd0 = a;
2230ec3b04cSmiod 		rmd.rmd1 = LE_R1_OWN | LE_R1_ONES | (-LEBLEN & 0xfff);
2240ec3b04cSmiod 		rmd.rmd2 = 0;
2250ec3b04cSmiod 		rmd.rmd3 = 0;
2260ec3b04cSmiod 		(*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix),
2270ec3b04cSmiod 		    sizeof(rmd));
2280ec3b04cSmiod 	}
2290ec3b04cSmiod 
2300ec3b04cSmiod 	/*
2310ec3b04cSmiod 	 * Set up transmit ring descriptors.
2320ec3b04cSmiod 	 */
2330ec3b04cSmiod 	for (bix = 0; bix < sc->sc_ntbuf; bix++) {
2340ec3b04cSmiod 		a = sc->sc_addr + LE_TBUFADDR(sc, bix);
2350ec3b04cSmiod 		tmd.tmd0 = a;
2360ec3b04cSmiod 		tmd.tmd1 = LE_T1_ONES;
2370ec3b04cSmiod 		tmd.tmd2 = 0;
2380ec3b04cSmiod 		tmd.tmd3 = 0;
2390ec3b04cSmiod 		(*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix),
2400ec3b04cSmiod 		    sizeof(tmd));
2410ec3b04cSmiod 	}
2420ec3b04cSmiod }
2430ec3b04cSmiod 
2440ec3b04cSmiod void
am79900_rint(struct lance_softc * sc)2450ec3b04cSmiod am79900_rint(struct lance_softc *sc)
2460ec3b04cSmiod {
2470ec3b04cSmiod 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
2480d918b5eSmpi 	struct mbuf *m;
2490d918b5eSmpi 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
2500ec3b04cSmiod 	int bix;
2510ec3b04cSmiod 	int rp;
2520ec3b04cSmiod 	struct lermd rmd;
2530ec3b04cSmiod 
2540ec3b04cSmiod 	bix = sc->sc_last_rd;
2550ec3b04cSmiod 
2560ec3b04cSmiod 	/* Process all buffers with valid data. */
2570ec3b04cSmiod 	for (;;) {
2580ec3b04cSmiod 		rp = LE_RMDADDR(sc, bix);
2590ec3b04cSmiod 		(*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd));
2600ec3b04cSmiod 
2610ec3b04cSmiod 		if (rmd.rmd1 & LE_R1_OWN)
2620ec3b04cSmiod 			break;
2630ec3b04cSmiod 
2640ec3b04cSmiod 		if (rmd.rmd1 & LE_R1_ERR) {
2650ec3b04cSmiod 			if (rmd.rmd1 & LE_R1_ENP) {
2660ec3b04cSmiod #ifdef LEDEBUG
2670ec3b04cSmiod 				if ((rmd.rmd1 & LE_R1_OFLO) == 0) {
2680ec3b04cSmiod 					if (rmd.rmd1 & LE_R1_FRAM)
2690ec3b04cSmiod 						printf("%s: framing error\n",
2700ec3b04cSmiod 						    sc->sc_dev.dv_xname);
2710ec3b04cSmiod 					if (rmd.rmd1 & LE_R1_CRC)
2720ec3b04cSmiod 						printf("%s: crc mismatch\n",
2730ec3b04cSmiod 						    sc->sc_dev.dv_xname);
2740ec3b04cSmiod 				}
2750ec3b04cSmiod #endif
2760ec3b04cSmiod 			} else {
2770ec3b04cSmiod 				if (rmd.rmd1 & LE_R1_OFLO)
2780ec3b04cSmiod 					printf("%s: overflow\n",
2790ec3b04cSmiod 					    sc->sc_dev.dv_xname);
2800ec3b04cSmiod 			}
2810ec3b04cSmiod 			if (rmd.rmd1 & LE_R1_BUFF)
2820ec3b04cSmiod 				printf("%s: receive buffer error\n",
2830ec3b04cSmiod 				    sc->sc_dev.dv_xname);
2840ec3b04cSmiod 			ifp->if_ierrors++;
2850ec3b04cSmiod 		} else if ((rmd.rmd1 & (LE_R1_STP | LE_R1_ENP)) !=
2860ec3b04cSmiod 		    (LE_R1_STP | LE_R1_ENP)) {
2870ec3b04cSmiod 			printf("%s: dropping chained buffer\n",
2880ec3b04cSmiod 			    sc->sc_dev.dv_xname);
2890ec3b04cSmiod 			ifp->if_ierrors++;
2900ec3b04cSmiod 		} else {
2910ec3b04cSmiod #ifdef LEDEBUG
2920ec3b04cSmiod 			if (sc->sc_debug > 1)
2930ec3b04cSmiod 				am79900_recv_print(sc, sc->sc_last_rd);
2940ec3b04cSmiod #endif
2950d918b5eSmpi 			m = lance_read(sc, LE_RBUFADDR(sc, bix),
2960ec3b04cSmiod 			    (rmd.rmd2  & 0xfff) - 4);
2970d918b5eSmpi 			if (m != NULL)
2980d918b5eSmpi 				ml_enqueue(&ml, m);
2990ec3b04cSmiod 		}
3000ec3b04cSmiod 
3010ec3b04cSmiod 		rmd.rmd1 = LE_R1_OWN | LE_R1_ONES | (-LEBLEN & 0xfff);
3020ec3b04cSmiod 		rmd.rmd2 = 0;
3030ec3b04cSmiod 		rmd.rmd3 = 0;
3040ec3b04cSmiod 		(*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd));
3050ec3b04cSmiod 
3060ec3b04cSmiod #ifdef LEDEBUG
3070ec3b04cSmiod 		if (sc->sc_debug)
3080ec3b04cSmiod 			printf("sc->sc_last_rd = %x, rmd: "
3090ec3b04cSmiod 			       "adr %08x, flags/blen %08x\n",
3100ec3b04cSmiod 				sc->sc_last_rd,
3110ec3b04cSmiod 				rmd.rmd0, rmd.rmd1);
3120ec3b04cSmiod #endif
3130ec3b04cSmiod 
3140ec3b04cSmiod 		if (++bix == sc->sc_nrbuf)
3150ec3b04cSmiod 			bix = 0;
3160ec3b04cSmiod 	}
3170ec3b04cSmiod 
3180ec3b04cSmiod 	sc->sc_last_rd = bix;
3190d918b5eSmpi 
3200d918b5eSmpi 	if_input(ifp, &ml);
3210ec3b04cSmiod }
3220ec3b04cSmiod 
3230ec3b04cSmiod void
am79900_tint(struct lance_softc * sc)3240ec3b04cSmiod am79900_tint(struct lance_softc *sc)
3250ec3b04cSmiod {
3260ec3b04cSmiod 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
3270ec3b04cSmiod 	int bix;
3280ec3b04cSmiod 	struct letmd tmd;
3290ec3b04cSmiod 
3300ec3b04cSmiod 	bix = sc->sc_first_td;
3310ec3b04cSmiod 
3320ec3b04cSmiod 	for (;;) {
3330ec3b04cSmiod 		if (sc->sc_no_td <= 0)
3340ec3b04cSmiod 			break;
3350ec3b04cSmiod 
3360ec3b04cSmiod 		(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix),
3370ec3b04cSmiod 		    sizeof(tmd));
3380ec3b04cSmiod 
3390ec3b04cSmiod #ifdef LEDEBUG
3400ec3b04cSmiod 		if (sc->sc_debug)
3410ec3b04cSmiod 			printf("trans tmd: "
3420ec3b04cSmiod 			    "adr %08x, flags/blen %08x\n",
3430ec3b04cSmiod 			    tmd.tmd0, tmd.tmd1);
3440ec3b04cSmiod #endif
3450ec3b04cSmiod 
3460ec3b04cSmiod 		if (tmd.tmd1 & LE_T1_OWN)
3470ec3b04cSmiod 			break;
3480ec3b04cSmiod 
349de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
3500ec3b04cSmiod 
3510ec3b04cSmiod 		if (tmd.tmd1 & LE_T1_ERR) {
3520ec3b04cSmiod 			if (tmd.tmd2 & LE_T2_BUFF)
3530ec3b04cSmiod 				printf("%s: transmit buffer error\n",
3540ec3b04cSmiod 				    sc->sc_dev.dv_xname);
3550ec3b04cSmiod 			else if (tmd.tmd2 & LE_T2_UFLO)
3560ec3b04cSmiod 				printf("%s: underflow\n", sc->sc_dev.dv_xname);
3570ec3b04cSmiod 			if (tmd.tmd2 & (LE_T2_BUFF | LE_T2_UFLO)) {
3580ec3b04cSmiod 				lance_reset(sc);
3590ec3b04cSmiod 				return;
3600ec3b04cSmiod 			}
3610ec3b04cSmiod 			if (tmd.tmd2 & LE_T2_LCAR) {
3620ec3b04cSmiod 				sc->sc_havecarrier = 0;
3630ec3b04cSmiod 				if (sc->sc_nocarrier)
3640ec3b04cSmiod 					(*sc->sc_nocarrier)(sc);
3650ec3b04cSmiod 				else
3660ec3b04cSmiod 					printf("%s: lost carrier\n",
3670ec3b04cSmiod 					    sc->sc_dev.dv_xname);
3680ec3b04cSmiod 			}
3690ec3b04cSmiod 			if (tmd.tmd2 & LE_T2_LCOL)
3700ec3b04cSmiod 				ifp->if_collisions++;
3710ec3b04cSmiod 			if (tmd.tmd2 & LE_T2_RTRY) {
3720ec3b04cSmiod #ifdef LEDEBUG
3730ec3b04cSmiod 				printf("%s: excessive collisions\n",
3740ec3b04cSmiod 				    sc->sc_dev.dv_xname);
3750ec3b04cSmiod #endif
3760ec3b04cSmiod 				ifp->if_collisions += 16;
3770ec3b04cSmiod 			}
3780ec3b04cSmiod 			ifp->if_oerrors++;
3790ec3b04cSmiod 		} else {
3800ec3b04cSmiod 			if (tmd.tmd1 & LE_T1_ONE)
3810ec3b04cSmiod 				ifp->if_collisions++;
3820ec3b04cSmiod 			else if (tmd.tmd1 & LE_T1_MORE)
3830ec3b04cSmiod 				/* Real number is unknown. */
3840ec3b04cSmiod 				ifp->if_collisions += 2;
3850ec3b04cSmiod 		}
3860ec3b04cSmiod 
3870ec3b04cSmiod 		if (++bix == sc->sc_ntbuf)
3880ec3b04cSmiod 			bix = 0;
3890ec3b04cSmiod 
3900ec3b04cSmiod 		--sc->sc_no_td;
3910ec3b04cSmiod 	}
3920ec3b04cSmiod 
3930ec3b04cSmiod 	sc->sc_first_td = bix;
3940ec3b04cSmiod 
3950ec3b04cSmiod 	am79900_start(ifp);
3960ec3b04cSmiod 
3970ec3b04cSmiod 	if (sc->sc_no_td == 0)
3980ec3b04cSmiod 		ifp->if_timer = 0;
3990ec3b04cSmiod }
4000ec3b04cSmiod 
4010ec3b04cSmiod /*
4020ec3b04cSmiod  * Controller interrupt.
4030ec3b04cSmiod  */
4040ec3b04cSmiod int
am79900_intr(void * arg)4050ec3b04cSmiod am79900_intr(void *arg)
4060ec3b04cSmiod {
4070ec3b04cSmiod 	struct lance_softc *sc = arg;
4080ec3b04cSmiod 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
4090ec3b04cSmiod 	uint16_t isr;
4100ec3b04cSmiod 
4110ec3b04cSmiod 	isr = (*sc->sc_rdcsr)(sc, LE_CSR0) | sc->sc_saved_csr0;
4120ec3b04cSmiod 	sc->sc_saved_csr0 = 0;
4130ec3b04cSmiod #if defined(LEDEBUG) && LEDEBUG > 1
4140ec3b04cSmiod 	if (sc->sc_debug)
4150ec3b04cSmiod 		printf("%s: am79900_intr entering with isr=%04x\n",
4160ec3b04cSmiod 		    sc->sc_dev.dv_xname, isr);
4170ec3b04cSmiod #endif
4180ec3b04cSmiod 	if ((isr & LE_C0_INTR) == 0)
4190ec3b04cSmiod 		return (0);
4200ec3b04cSmiod 
4210ec3b04cSmiod 	(*sc->sc_wrcsr)(sc, LE_CSR0,
4220ec3b04cSmiod 	    isr & (LE_C0_INEA | LE_C0_BABL | LE_C0_MISS | LE_C0_MERR |
4230ec3b04cSmiod 		   LE_C0_RINT | LE_C0_TINT | LE_C0_IDON));
4240ec3b04cSmiod 	if (isr & LE_C0_ERR) {
4250ec3b04cSmiod 		if (isr & LE_C0_BABL) {
4260ec3b04cSmiod #ifdef LEDEBUG
4270ec3b04cSmiod 			printf("%s: babble\n", sc->sc_dev.dv_xname);
4280ec3b04cSmiod #endif
4290ec3b04cSmiod 			ifp->if_oerrors++;
4300ec3b04cSmiod 		}
4310ec3b04cSmiod #if 0
4320ec3b04cSmiod 		if (isr & LE_C0_CERR) {
4330ec3b04cSmiod 			printf("%s: collision error\n",
4340ec3b04cSmiod 			    sc->sc_dev.dv_xname);
4350ec3b04cSmiod 			ifp->if_collisions++;
4360ec3b04cSmiod 		}
4370ec3b04cSmiod #endif
4380ec3b04cSmiod 		if (isr & LE_C0_MISS) {
4390ec3b04cSmiod #ifdef LEDEBUG
4400ec3b04cSmiod 			printf("%s: missed packet\n", sc->sc_dev.dv_xname);
4410ec3b04cSmiod #endif
4420ec3b04cSmiod 			ifp->if_ierrors++;
4430ec3b04cSmiod 		}
4440ec3b04cSmiod 		if (isr & LE_C0_MERR) {
4450ec3b04cSmiod 			printf("%s: memory error\n", sc->sc_dev.dv_xname);
4460ec3b04cSmiod 			lance_reset(sc);
4470ec3b04cSmiod 			return (1);
4480ec3b04cSmiod 		}
4490ec3b04cSmiod 	}
4500ec3b04cSmiod 
4510ec3b04cSmiod 	if ((isr & LE_C0_RXON) == 0) {
4520ec3b04cSmiod 		printf("%s: receiver disabled\n", sc->sc_dev.dv_xname);
4530ec3b04cSmiod 		ifp->if_ierrors++;
4540ec3b04cSmiod 		lance_reset(sc);
4550ec3b04cSmiod 		return (1);
4560ec3b04cSmiod 	}
4570ec3b04cSmiod 	if ((isr & LE_C0_TXON) == 0) {
4580ec3b04cSmiod 		printf("%s: transmitter disabled\n", sc->sc_dev.dv_xname);
4590ec3b04cSmiod 		ifp->if_oerrors++;
4600ec3b04cSmiod 		lance_reset(sc);
4610ec3b04cSmiod 		return (1);
4620ec3b04cSmiod 	}
4630ec3b04cSmiod 
4640ec3b04cSmiod 	/*
4650ec3b04cSmiod 	 * Pretend we have carrier; if we don't this will be cleared
4660ec3b04cSmiod 	 * shortly.
4670ec3b04cSmiod 	 */
4680ec3b04cSmiod 	sc->sc_havecarrier = 1;
4690ec3b04cSmiod 
4700ec3b04cSmiod 	if (isr & LE_C0_RINT)
4710ec3b04cSmiod 		am79900_rint(sc);
4720ec3b04cSmiod 	if (isr & LE_C0_TINT)
4730ec3b04cSmiod 		am79900_tint(sc);
4740ec3b04cSmiod 
4750ec3b04cSmiod 	return (1);
4760ec3b04cSmiod }
4770ec3b04cSmiod 
4780ec3b04cSmiod /*
4790ec3b04cSmiod  * Setup output on interface.
4800ec3b04cSmiod  * Get another datagram to send off of the interface queue, and map it to the
4810ec3b04cSmiod  * interface before starting the output.
4820ec3b04cSmiod  * Called only at splnet or interrupt level.
4830ec3b04cSmiod  */
4840ec3b04cSmiod void
am79900_start(struct ifnet * ifp)4850ec3b04cSmiod am79900_start(struct ifnet *ifp)
4860ec3b04cSmiod {
4870ec3b04cSmiod 	struct lance_softc *sc = ifp->if_softc;
4880ec3b04cSmiod 	int bix;
4890ec3b04cSmiod 	struct mbuf *m;
4900ec3b04cSmiod 	struct letmd tmd;
4910ec3b04cSmiod 	int rp;
4920ec3b04cSmiod 	int len;
4930ec3b04cSmiod 
494de6cd8fbSdlg 	if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
4950ec3b04cSmiod 		return;
4960ec3b04cSmiod 
4970ec3b04cSmiod 	bix = sc->sc_last_td;
4980ec3b04cSmiod 
4990ec3b04cSmiod 	for (;;) {
5000ec3b04cSmiod 		rp = LE_TMDADDR(sc, bix);
5010ec3b04cSmiod 		(*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd));
5020ec3b04cSmiod 
5030ec3b04cSmiod 		if (tmd.tmd1 & LE_T1_OWN) {
504de6cd8fbSdlg 			ifq_set_oactive(&ifp->if_snd);
5050ec3b04cSmiod 			printf("missing buffer, no_td = %d, last_td = %d\n",
5060ec3b04cSmiod 			    sc->sc_no_td, sc->sc_last_td);
5070ec3b04cSmiod 		}
5080ec3b04cSmiod 
509*63bcfa73Spatrick 		m = ifq_dequeue(&ifp->if_snd);
51064a3f76cSjsg 		if (m == NULL)
5110ec3b04cSmiod 			break;
5120ec3b04cSmiod 
5130ec3b04cSmiod #if NBPFILTER > 0
5140ec3b04cSmiod 		/*
5150ec3b04cSmiod 		 * If BPF is listening on this interface, let it see the packet
5160ec3b04cSmiod 		 * before we commit it to the wire.
5170ec3b04cSmiod 		 */
5180ec3b04cSmiod 		if (ifp->if_bpf)
5190ec3b04cSmiod 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
5200ec3b04cSmiod #endif
5210ec3b04cSmiod 
5220ec3b04cSmiod 		/*
5230ec3b04cSmiod 		 * Copy the mbuf chain into the transmit buffer.
5240ec3b04cSmiod 		 */
5250ec3b04cSmiod 		len = lance_put(sc, LE_TBUFADDR(sc, bix), m);
5260ec3b04cSmiod 
5270ec3b04cSmiod #ifdef LEDEBUG
5280ec3b04cSmiod 		if (len > ETHERMTU + sizeof(struct ether_header))
5290ec3b04cSmiod 			printf("packet length %d\n", len);
5300ec3b04cSmiod #endif
5310ec3b04cSmiod 
5320ec3b04cSmiod 		ifp->if_timer = 5;
5330ec3b04cSmiod 
5340ec3b04cSmiod 		/*
5350ec3b04cSmiod 		 * Init transmit registers, and set transmit start flag.
5360ec3b04cSmiod 		 */
5370ec3b04cSmiod 		tmd.tmd1 = LE_T1_OWN | LE_T1_STP | LE_T1_ENP | LE_T1_ONES |
5380ec3b04cSmiod 		    (-len & 0xfff);
5390ec3b04cSmiod 		tmd.tmd2 = 0;
5400ec3b04cSmiod 		tmd.tmd3 = 0;
5410ec3b04cSmiod 
5420ec3b04cSmiod 		(*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd));
5430ec3b04cSmiod 
5440ec3b04cSmiod #ifdef LEDEBUG
5450ec3b04cSmiod 		if (sc->sc_debug > 1)
5460ec3b04cSmiod 			am79900_xmit_print(sc, sc->sc_last_td);
5470ec3b04cSmiod #endif
5480ec3b04cSmiod 
5490ec3b04cSmiod 		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
5500ec3b04cSmiod 
5510ec3b04cSmiod 		if (++bix == sc->sc_ntbuf)
5520ec3b04cSmiod 			bix = 0;
5530ec3b04cSmiod 
5540ec3b04cSmiod 		if (++sc->sc_no_td == sc->sc_ntbuf) {
555de6cd8fbSdlg 			ifq_set_oactive(&ifp->if_snd);
5560ec3b04cSmiod 			break;
5570ec3b04cSmiod 		}
5580ec3b04cSmiod 
5590ec3b04cSmiod 	}
5600ec3b04cSmiod 
5610ec3b04cSmiod 	sc->sc_last_td = bix;
5620ec3b04cSmiod }
5630ec3b04cSmiod 
5640ec3b04cSmiod #ifdef LEDEBUG
5650ec3b04cSmiod void
am79900_recv_print(struct lance_softc * sc,int no)5660ec3b04cSmiod am79900_recv_print(struct lance_softc *sc, int no)
5670ec3b04cSmiod {
5680ec3b04cSmiod 	struct lermd rmd;
5690ec3b04cSmiod 	uint16_t len;
5700ec3b04cSmiod 	struct ether_header eh;
5710ec3b04cSmiod 
5720ec3b04cSmiod 	(*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd));
5730ec3b04cSmiod 	len = (rmd.rmd2  & 0xfff) - 4;
5740ec3b04cSmiod 	printf("%s: receive buffer %d, len = %d\n",
5750ec3b04cSmiod 	    sc->sc_dev.dv_xname, no, len);
5760ec3b04cSmiod 	printf("%s: status %04x\n", sc->sc_dev.dv_xname,
5770ec3b04cSmiod 	    (*sc->sc_rdcsr)(sc, LE_CSR0));
5780ec3b04cSmiod 	printf("%s: adr %08x, flags/blen %08x\n",
5790ec3b04cSmiod 	    sc->sc_dev.dv_xname, rmd.rmd0, rmd.rmd1);
5800ec3b04cSmiod 	if (len >= sizeof(eh)) {
5810ec3b04cSmiod 		(*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh));
5820ec3b04cSmiod 		printf("%s: dst %s", sc->sc_dev.dv_xname,
5830ec3b04cSmiod 			ether_sprintf(eh.ether_dhost));
5840ec3b04cSmiod 		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
5850ec3b04cSmiod 			ntohs(eh.ether_type));
5860ec3b04cSmiod 	}
5870ec3b04cSmiod }
5880ec3b04cSmiod 
5890ec3b04cSmiod void
am79900_xmit_print(struct lance_softc * sc,int no)5900ec3b04cSmiod am79900_xmit_print(struct lance_softc *sc, int no)
5910ec3b04cSmiod {
5920ec3b04cSmiod 	struct letmd tmd;
5930ec3b04cSmiod 	uint16_t len;
5940ec3b04cSmiod 	struct ether_header eh;
5950ec3b04cSmiod 
5960ec3b04cSmiod 	(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd));
5970ec3b04cSmiod 	len = -(tmd.tmd1 & 0xfff);
5980ec3b04cSmiod 	printf("%s: transmit buffer %d, len = %d\n",
5990ec3b04cSmiod 	    sc->sc_dev.dv_xname, no, len);
6000ec3b04cSmiod 	printf("%s: status %04x\n", sc->sc_dev.dv_xname,
6010ec3b04cSmiod 	    (*sc->sc_rdcsr)(sc, LE_CSR0));
6020ec3b04cSmiod 	printf("%s: adr %08x, flags/blen %08x\n",
6030ec3b04cSmiod 	    sc->sc_dev.dv_xname, tmd.tmd0, tmd.tmd1);
6040ec3b04cSmiod 	if (len >= sizeof(eh)) {
6050ec3b04cSmiod 		(*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh));
6060ec3b04cSmiod 		printf("%s: dst %s", sc->sc_dev.dv_xname,
6070ec3b04cSmiod 			ether_sprintf(eh.ether_dhost));
6080ec3b04cSmiod 		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
6090ec3b04cSmiod 		    ntohs(eh.ether_type));
6100ec3b04cSmiod 	}
6110ec3b04cSmiod }
6120ec3b04cSmiod #endif /* LEDEBUG */
613