1a7ee7a7dSMarius Strobl /* $NetBSD: am7990.c,v 1.68 2005/12/11 12:21:25 christos Exp $ */ 2a7ee7a7dSMarius Strobl 3a7ee7a7dSMarius Strobl /*- 4a7ee7a7dSMarius Strobl * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 5a7ee7a7dSMarius Strobl * All rights reserved. 6a7ee7a7dSMarius Strobl * 7a7ee7a7dSMarius Strobl * This code is derived from software contributed to The NetBSD Foundation 8a7ee7a7dSMarius Strobl * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace 9a7ee7a7dSMarius Strobl * Simulation Facility, NASA Ames Research Center. 10a7ee7a7dSMarius Strobl * 11a7ee7a7dSMarius Strobl * Redistribution and use in source and binary forms, with or without 12a7ee7a7dSMarius Strobl * modification, are permitted provided that the following conditions 13a7ee7a7dSMarius Strobl * are met: 14a7ee7a7dSMarius Strobl * 1. Redistributions of source code must retain the above copyright 15a7ee7a7dSMarius Strobl * notice, this list of conditions and the following disclaimer. 16a7ee7a7dSMarius Strobl * 2. Redistributions in binary form must reproduce the above copyright 17a7ee7a7dSMarius Strobl * notice, this list of conditions and the following disclaimer in the 18a7ee7a7dSMarius Strobl * documentation and/or other materials provided with the distribution. 19a7ee7a7dSMarius Strobl * 3. All advertising materials mentioning features or use of this software 20a7ee7a7dSMarius Strobl * must display the following acknowledgement: 21a7ee7a7dSMarius Strobl * This product includes software developed by the NetBSD 22a7ee7a7dSMarius Strobl * Foundation, Inc. and its contributors. 23a7ee7a7dSMarius Strobl * 4. Neither the name of The NetBSD Foundation nor the names of its 24a7ee7a7dSMarius Strobl * contributors may be used to endorse or promote products derived 25a7ee7a7dSMarius Strobl * from this software without specific prior written permission. 26a7ee7a7dSMarius Strobl * 27a7ee7a7dSMarius Strobl * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28a7ee7a7dSMarius Strobl * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29a7ee7a7dSMarius Strobl * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30a7ee7a7dSMarius Strobl * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31a7ee7a7dSMarius Strobl * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32a7ee7a7dSMarius Strobl * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33a7ee7a7dSMarius Strobl * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34a7ee7a7dSMarius Strobl * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35a7ee7a7dSMarius Strobl * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36a7ee7a7dSMarius Strobl * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37a7ee7a7dSMarius Strobl * POSSIBILITY OF SUCH DAMAGE. 38a7ee7a7dSMarius Strobl */ 39a7ee7a7dSMarius Strobl 40a7ee7a7dSMarius Strobl /*- 41a7ee7a7dSMarius Strobl * Copyright (c) 1992, 1993 42a7ee7a7dSMarius Strobl * The Regents of the University of California. All rights reserved. 43a7ee7a7dSMarius Strobl * 44a7ee7a7dSMarius Strobl * This code is derived from software contributed to Berkeley by 45a7ee7a7dSMarius Strobl * Ralph Campbell and Rick Macklem. 46a7ee7a7dSMarius Strobl * 47a7ee7a7dSMarius Strobl * Redistribution and use in source and binary forms, with or without 48a7ee7a7dSMarius Strobl * modification, are permitted provided that the following conditions 49a7ee7a7dSMarius Strobl * are met: 50a7ee7a7dSMarius Strobl * 1. Redistributions of source code must retain the above copyright 51a7ee7a7dSMarius Strobl * notice, this list of conditions and the following disclaimer. 52a7ee7a7dSMarius Strobl * 2. Redistributions in binary form must reproduce the above copyright 53a7ee7a7dSMarius Strobl * notice, this list of conditions and the following disclaimer in the 54a7ee7a7dSMarius Strobl * documentation and/or other materials provided with the distribution. 55a7ee7a7dSMarius Strobl * 3. Neither the name of the University nor the names of its contributors 56a7ee7a7dSMarius Strobl * may be used to endorse or promote products derived from this software 57a7ee7a7dSMarius Strobl * without specific prior written permission. 58a7ee7a7dSMarius Strobl * 59a7ee7a7dSMarius Strobl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60a7ee7a7dSMarius Strobl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61a7ee7a7dSMarius Strobl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62a7ee7a7dSMarius Strobl * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63a7ee7a7dSMarius Strobl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64a7ee7a7dSMarius Strobl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65a7ee7a7dSMarius Strobl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66a7ee7a7dSMarius Strobl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67a7ee7a7dSMarius Strobl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68a7ee7a7dSMarius Strobl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69a7ee7a7dSMarius Strobl * SUCH DAMAGE. 70a7ee7a7dSMarius Strobl * 71a7ee7a7dSMarius Strobl * @(#)if_le.c 8.2 (Berkeley) 11/16/93 72a7ee7a7dSMarius Strobl */ 73a7ee7a7dSMarius Strobl 74a7ee7a7dSMarius Strobl #include <sys/cdefs.h> 75a7ee7a7dSMarius Strobl __FBSDID("$FreeBSD$"); 76a7ee7a7dSMarius Strobl 77a7ee7a7dSMarius Strobl #include <sys/param.h> 78a7ee7a7dSMarius Strobl #include <sys/bus.h> 79a7ee7a7dSMarius Strobl #include <sys/endian.h> 80a7ee7a7dSMarius Strobl #include <sys/lock.h> 81a7ee7a7dSMarius Strobl #include <sys/mbuf.h> 82a7ee7a7dSMarius Strobl #include <sys/mutex.h> 83a7ee7a7dSMarius Strobl #include <sys/socket.h> 84a7ee7a7dSMarius Strobl 85a7ee7a7dSMarius Strobl #include <net/bpf.h> 86a7ee7a7dSMarius Strobl #include <net/ethernet.h> 87a7ee7a7dSMarius Strobl #include <net/if.h> 88a7ee7a7dSMarius Strobl #include <net/if_arp.h> 89a7ee7a7dSMarius Strobl #include <net/if_dl.h> 90a7ee7a7dSMarius Strobl #include <net/if_media.h> 91a7ee7a7dSMarius Strobl #include <net/if_var.h> 92a7ee7a7dSMarius Strobl 9360c430f5SMarius Strobl #include <machine/bus.h> 9460c430f5SMarius Strobl 95a7ee7a7dSMarius Strobl #include <dev/le/lancereg.h> 96a7ee7a7dSMarius Strobl #include <dev/le/lancevar.h> 97a7ee7a7dSMarius Strobl #include <dev/le/am7990reg.h> 98a7ee7a7dSMarius Strobl #include <dev/le/am7990var.h> 99a7ee7a7dSMarius Strobl 100a7ee7a7dSMarius Strobl static void am7990_meminit(struct lance_softc *); 101a7ee7a7dSMarius Strobl static void am7990_rint(struct lance_softc *); 102a7ee7a7dSMarius Strobl static void am7990_tint(struct lance_softc *); 103a7ee7a7dSMarius Strobl static void am7990_start_locked(struct lance_softc *sc); 104a7ee7a7dSMarius Strobl 105a7ee7a7dSMarius Strobl #ifdef LEDEBUG 106a7ee7a7dSMarius Strobl static void am7990_recv_print(struct lance_softc *, int); 107a7ee7a7dSMarius Strobl static void am7990_xmit_print(struct lance_softc *, int); 108a7ee7a7dSMarius Strobl #endif 109a7ee7a7dSMarius Strobl 110a7ee7a7dSMarius Strobl int 111a7ee7a7dSMarius Strobl am7990_config(struct am7990_softc *sc, const char* name, int unit) 112a7ee7a7dSMarius Strobl { 113a7ee7a7dSMarius Strobl int error, mem; 114a7ee7a7dSMarius Strobl 115a7ee7a7dSMarius Strobl sc->lsc.sc_meminit = am7990_meminit; 116a7ee7a7dSMarius Strobl sc->lsc.sc_start_locked = am7990_start_locked; 117a7ee7a7dSMarius Strobl 118a7ee7a7dSMarius Strobl error = lance_config(&sc->lsc, name, unit); 119a7ee7a7dSMarius Strobl if (error != 0) 120a7ee7a7dSMarius Strobl return (error); 121a7ee7a7dSMarius Strobl 122a7ee7a7dSMarius Strobl mem = 0; 123a7ee7a7dSMarius Strobl sc->lsc.sc_initaddr = mem; 124a7ee7a7dSMarius Strobl mem += sizeof(struct leinit); 125a7ee7a7dSMarius Strobl sc->lsc.sc_rmdaddr = mem; 126a7ee7a7dSMarius Strobl mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf; 127a7ee7a7dSMarius Strobl sc->lsc.sc_tmdaddr = mem; 128a7ee7a7dSMarius Strobl mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf; 129a7ee7a7dSMarius Strobl sc->lsc.sc_rbufaddr = mem; 130a7ee7a7dSMarius Strobl mem += LEBLEN * sc->lsc.sc_nrbuf; 131a7ee7a7dSMarius Strobl sc->lsc.sc_tbufaddr = mem; 132a7ee7a7dSMarius Strobl mem += LEBLEN * sc->lsc.sc_ntbuf; 133a7ee7a7dSMarius Strobl 134a7ee7a7dSMarius Strobl if (mem > sc->lsc.sc_memsize) 135a7ee7a7dSMarius Strobl panic("%s: memsize", __func__); 136a7ee7a7dSMarius Strobl 137a7ee7a7dSMarius Strobl lance_attach(&sc->lsc); 138a7ee7a7dSMarius Strobl 139a7ee7a7dSMarius Strobl return (0); 140a7ee7a7dSMarius Strobl } 141a7ee7a7dSMarius Strobl 142a7ee7a7dSMarius Strobl void 143a7ee7a7dSMarius Strobl am7990_detach(struct am7990_softc *sc) 144a7ee7a7dSMarius Strobl { 145a7ee7a7dSMarius Strobl 146a7ee7a7dSMarius Strobl lance_detach(&sc->lsc); 147a7ee7a7dSMarius Strobl } 148a7ee7a7dSMarius Strobl 149a7ee7a7dSMarius Strobl /* 150a7ee7a7dSMarius Strobl * Set up the initialization block and the descriptor rings. 151a7ee7a7dSMarius Strobl */ 152a7ee7a7dSMarius Strobl static void 153a7ee7a7dSMarius Strobl am7990_meminit(struct lance_softc *sc) 154a7ee7a7dSMarius Strobl { 155a7ee7a7dSMarius Strobl struct ifnet *ifp = sc->sc_ifp; 156a7ee7a7dSMarius Strobl struct leinit init; 157a7ee7a7dSMarius Strobl struct lermd rmd; 158a7ee7a7dSMarius Strobl struct letmd tmd; 159a7ee7a7dSMarius Strobl u_long a; 160a7ee7a7dSMarius Strobl int bix; 161a7ee7a7dSMarius Strobl 162a7ee7a7dSMarius Strobl LE_LOCK_ASSERT(sc, MA_OWNED); 163a7ee7a7dSMarius Strobl 164a7ee7a7dSMarius Strobl if (ifp->if_flags & IFF_PROMISC) 165a7ee7a7dSMarius Strobl init.init_mode = LE_MODE_NORMAL | LE_MODE_PROM; 166a7ee7a7dSMarius Strobl else 167a7ee7a7dSMarius Strobl init.init_mode = LE_MODE_NORMAL; 168a7ee7a7dSMarius Strobl 169a7ee7a7dSMarius Strobl init.init_padr[0] = (sc->sc_enaddr[1] << 8) | sc->sc_enaddr[0]; 170a7ee7a7dSMarius Strobl init.init_padr[1] = (sc->sc_enaddr[3] << 8) | sc->sc_enaddr[2]; 171a7ee7a7dSMarius Strobl init.init_padr[2] = (sc->sc_enaddr[5] << 8) | sc->sc_enaddr[4]; 172a7ee7a7dSMarius Strobl lance_setladrf(sc, init.init_ladrf); 173a7ee7a7dSMarius Strobl 174a7ee7a7dSMarius Strobl sc->sc_last_rd = 0; 175a7ee7a7dSMarius Strobl sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0; 176a7ee7a7dSMarius Strobl 177a7ee7a7dSMarius Strobl a = sc->sc_addr + LE_RMDADDR(sc, 0); 178a7ee7a7dSMarius Strobl init.init_rdra = a; 179a7ee7a7dSMarius Strobl init.init_rlen = (a >> 16) | ((ffs(sc->sc_nrbuf) - 1) << 13); 180a7ee7a7dSMarius Strobl 181a7ee7a7dSMarius Strobl a = sc->sc_addr + LE_TMDADDR(sc, 0); 182a7ee7a7dSMarius Strobl init.init_tdra = a; 183a7ee7a7dSMarius Strobl init.init_tlen = (a >> 16) | ((ffs(sc->sc_ntbuf) - 1) << 13); 184a7ee7a7dSMarius Strobl 185a7ee7a7dSMarius Strobl (*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init)); 186a7ee7a7dSMarius Strobl 187a7ee7a7dSMarius Strobl /* 188a7ee7a7dSMarius Strobl * Set up receive ring descriptors. 189a7ee7a7dSMarius Strobl */ 190a7ee7a7dSMarius Strobl for (bix = 0; bix < sc->sc_nrbuf; bix++) { 191a7ee7a7dSMarius Strobl a = sc->sc_addr + LE_RBUFADDR(sc, bix); 192a7ee7a7dSMarius Strobl rmd.rmd0 = a; 193a7ee7a7dSMarius Strobl rmd.rmd1_hadr = a >> 16; 194a7ee7a7dSMarius Strobl rmd.rmd1_bits = LE_R1_OWN; 195a7ee7a7dSMarius Strobl rmd.rmd2 = -LEBLEN | LE_XMD2_ONES; 196a7ee7a7dSMarius Strobl rmd.rmd3 = 0; 197a7ee7a7dSMarius Strobl (*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix), 198a7ee7a7dSMarius Strobl sizeof(rmd)); 199a7ee7a7dSMarius Strobl } 200a7ee7a7dSMarius Strobl 201a7ee7a7dSMarius Strobl /* 202a7ee7a7dSMarius Strobl * Set up transmit ring descriptors. 203a7ee7a7dSMarius Strobl */ 204a7ee7a7dSMarius Strobl for (bix = 0; bix < sc->sc_ntbuf; bix++) { 205a7ee7a7dSMarius Strobl a = sc->sc_addr + LE_TBUFADDR(sc, bix); 206a7ee7a7dSMarius Strobl tmd.tmd0 = a; 207a7ee7a7dSMarius Strobl tmd.tmd1_hadr = a >> 16; 208a7ee7a7dSMarius Strobl tmd.tmd1_bits = 0; 209a7ee7a7dSMarius Strobl tmd.tmd2 = LE_XMD2_ONES; 210a7ee7a7dSMarius Strobl tmd.tmd3 = 0; 211a7ee7a7dSMarius Strobl (*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix), 212a7ee7a7dSMarius Strobl sizeof(tmd)); 213a7ee7a7dSMarius Strobl } 214a7ee7a7dSMarius Strobl } 215a7ee7a7dSMarius Strobl 216a7ee7a7dSMarius Strobl static void 217a7ee7a7dSMarius Strobl am7990_rint(struct lance_softc *sc) 218a7ee7a7dSMarius Strobl { 219a7ee7a7dSMarius Strobl struct ifnet *ifp = sc->sc_ifp; 22060c430f5SMarius Strobl struct mbuf *m; 221a7ee7a7dSMarius Strobl struct lermd rmd; 222a7ee7a7dSMarius Strobl int bix, rp; 22360c430f5SMarius Strobl #if defined(LANCE_REVC_BUG) 22460c430f5SMarius Strobl struct ether_header *eh; 22560c430f5SMarius Strobl /* Make sure this is short-aligned, for ether_cmp(). */ 22660c430f5SMarius Strobl static uint16_t bcast_enaddr[3] = { ~0, ~0, ~0 }; 22760c430f5SMarius Strobl #endif 228a7ee7a7dSMarius Strobl 229a7ee7a7dSMarius Strobl bix = sc->sc_last_rd; 230a7ee7a7dSMarius Strobl 231a7ee7a7dSMarius Strobl /* Process all buffers with valid data. */ 232a7ee7a7dSMarius Strobl for (;;) { 233a7ee7a7dSMarius Strobl rp = LE_RMDADDR(sc, bix); 234a7ee7a7dSMarius Strobl (*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd)); 235a7ee7a7dSMarius Strobl 236a7ee7a7dSMarius Strobl if (rmd.rmd1_bits & LE_R1_OWN) 237a7ee7a7dSMarius Strobl break; 238a7ee7a7dSMarius Strobl 23960c430f5SMarius Strobl m = NULL; 24060c430f5SMarius Strobl if ((rmd.rmd1_bits & (LE_R1_ERR | LE_R1_STP | LE_R1_ENP)) != 24160c430f5SMarius Strobl (LE_R1_STP | LE_R1_ENP)) { 242a7ee7a7dSMarius Strobl if (rmd.rmd1_bits & LE_R1_ERR) { 243a7ee7a7dSMarius Strobl #ifdef LEDEBUG 24460c430f5SMarius Strobl if (rmd.rmd1_bits & LE_R1_ENP) { 245a7ee7a7dSMarius Strobl if ((rmd.rmd1_bits & LE_R1_OFLO) == 0) { 246a7ee7a7dSMarius Strobl if (rmd.rmd1_bits & LE_R1_FRAM) 247a7ee7a7dSMarius Strobl if_printf(ifp, 248a7ee7a7dSMarius Strobl "framing error\n"); 249a7ee7a7dSMarius Strobl if (rmd.rmd1_bits & LE_R1_CRC) 250a7ee7a7dSMarius Strobl if_printf(ifp, 251a7ee7a7dSMarius Strobl "crc mismatch\n"); 252a7ee7a7dSMarius Strobl } 25360c430f5SMarius Strobl } else 254a7ee7a7dSMarius Strobl if (rmd.rmd1_bits & LE_R1_OFLO) 255a7ee7a7dSMarius Strobl if_printf(ifp, "overflow\n"); 25660c430f5SMarius Strobl #endif 257a7ee7a7dSMarius Strobl if (rmd.rmd1_bits & LE_R1_BUFF) 25860c430f5SMarius Strobl if_printf(ifp, 25960c430f5SMarius Strobl "receive buffer error\n"); 260a7ee7a7dSMarius Strobl } else if ((rmd.rmd1_bits & (LE_R1_STP | LE_R1_ENP)) != 26160c430f5SMarius Strobl (LE_R1_STP | LE_R1_ENP)) 262a7ee7a7dSMarius Strobl if_printf(ifp, "dropping chained buffer\n"); 263a7ee7a7dSMarius Strobl } else { 264a7ee7a7dSMarius Strobl #ifdef LEDEBUG 265a7ee7a7dSMarius Strobl if (sc->sc_flags & LE_DEBUG) 26660c430f5SMarius Strobl am7990_recv_print(sc, bix); 267a7ee7a7dSMarius Strobl #endif 26860c430f5SMarius Strobl /* Pull the packet off the interface. */ 26960c430f5SMarius Strobl m = lance_get(sc, LE_RBUFADDR(sc, bix), 270a7ee7a7dSMarius Strobl (int)rmd.rmd3 - ETHER_CRC_LEN); 271a7ee7a7dSMarius Strobl } 272a7ee7a7dSMarius Strobl 273a7ee7a7dSMarius Strobl rmd.rmd1_bits = LE_R1_OWN; 274a7ee7a7dSMarius Strobl rmd.rmd2 = -LEBLEN | LE_XMD2_ONES; 275a7ee7a7dSMarius Strobl rmd.rmd3 = 0; 276a7ee7a7dSMarius Strobl (*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd)); 277a7ee7a7dSMarius Strobl 278a7ee7a7dSMarius Strobl if (++bix == sc->sc_nrbuf) 279a7ee7a7dSMarius Strobl bix = 0; 28060c430f5SMarius Strobl 28160c430f5SMarius Strobl if (m != NULL) { 28260c430f5SMarius Strobl ifp->if_ipackets++; 28360c430f5SMarius Strobl 28460c430f5SMarius Strobl #ifdef LANCE_REVC_BUG 28560c430f5SMarius Strobl /* 28660c430f5SMarius Strobl * The old LANCE (Rev. C) chips have a bug which 28760c430f5SMarius Strobl * causes garbage to be inserted in front of the 28860c430f5SMarius Strobl * received packet. The workaround is to ignore 28960c430f5SMarius Strobl * packets with an invalid destination address 29060c430f5SMarius Strobl * (garbage will usually not match). 29160c430f5SMarius Strobl * Of course, this precludes multicast support... 29260c430f5SMarius Strobl */ 29360c430f5SMarius Strobl eh = mtod(m, struct ether_header *); 29460c430f5SMarius Strobl if (ether_cmp(eh->ether_dhost, sc->sc_enaddr) && 29560c430f5SMarius Strobl ether_cmp(eh->ether_dhost, bcast_enaddr)) { 29660c430f5SMarius Strobl m_freem(m); 29760c430f5SMarius Strobl continue; 29860c430f5SMarius Strobl } 29960c430f5SMarius Strobl #endif 30060c430f5SMarius Strobl 30160c430f5SMarius Strobl /* Pass the packet up. */ 30260c430f5SMarius Strobl LE_UNLOCK(sc); 30360c430f5SMarius Strobl (*ifp->if_input)(ifp, m); 30460c430f5SMarius Strobl LE_LOCK(sc); 30560c430f5SMarius Strobl } else 30660c430f5SMarius Strobl ifp->if_ierrors++; 307a7ee7a7dSMarius Strobl } 308a7ee7a7dSMarius Strobl 309a7ee7a7dSMarius Strobl sc->sc_last_rd = bix; 310a7ee7a7dSMarius Strobl } 311a7ee7a7dSMarius Strobl 312a7ee7a7dSMarius Strobl static void 313a7ee7a7dSMarius Strobl am7990_tint(struct lance_softc *sc) 314a7ee7a7dSMarius Strobl { 315a7ee7a7dSMarius Strobl struct ifnet *ifp = sc->sc_ifp; 316a7ee7a7dSMarius Strobl struct letmd tmd; 317a7ee7a7dSMarius Strobl int bix; 318a7ee7a7dSMarius Strobl 319a7ee7a7dSMarius Strobl bix = sc->sc_first_td; 320a7ee7a7dSMarius Strobl 321a7ee7a7dSMarius Strobl for (;;) { 322a7ee7a7dSMarius Strobl if (sc->sc_no_td <= 0) 323a7ee7a7dSMarius Strobl break; 324a7ee7a7dSMarius Strobl 325a7ee7a7dSMarius Strobl (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix), 326a7ee7a7dSMarius Strobl sizeof(tmd)); 327a7ee7a7dSMarius Strobl 328a7ee7a7dSMarius Strobl #ifdef LEDEBUG 329a7ee7a7dSMarius Strobl if (sc->sc_flags & LE_DEBUG) 330a7ee7a7dSMarius Strobl if_printf(ifp, "trans tmd: " 331a7ee7a7dSMarius Strobl "ladr %04x, hadr %02x, flags %02x, " 332a7ee7a7dSMarius Strobl "bcnt %04x, mcnt %04x\n", 333a7ee7a7dSMarius Strobl tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, 334a7ee7a7dSMarius Strobl tmd.tmd2, tmd.tmd3); 335a7ee7a7dSMarius Strobl #endif 336a7ee7a7dSMarius Strobl 337a7ee7a7dSMarius Strobl if (tmd.tmd1_bits & LE_T1_OWN) 338a7ee7a7dSMarius Strobl break; 339a7ee7a7dSMarius Strobl 340a7ee7a7dSMarius Strobl ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 341a7ee7a7dSMarius Strobl 342a7ee7a7dSMarius Strobl if (tmd.tmd1_bits & LE_T1_ERR) { 343a7ee7a7dSMarius Strobl if (tmd.tmd3 & LE_T3_BUFF) 344a7ee7a7dSMarius Strobl if_printf(ifp, "transmit buffer error\n"); 345a7ee7a7dSMarius Strobl else if (tmd.tmd3 & LE_T3_UFLO) 346a7ee7a7dSMarius Strobl if_printf(ifp, "underflow\n"); 347a7ee7a7dSMarius Strobl if (tmd.tmd3 & (LE_T3_BUFF | LE_T3_UFLO)) { 348a7ee7a7dSMarius Strobl lance_init_locked(sc); 349a7ee7a7dSMarius Strobl return; 350a7ee7a7dSMarius Strobl } 351a7ee7a7dSMarius Strobl if (tmd.tmd3 & LE_T3_LCAR) { 352a7ee7a7dSMarius Strobl if (sc->sc_flags & LE_CARRIER) 353a7ee7a7dSMarius Strobl if_link_state_change(ifp, 354a7ee7a7dSMarius Strobl LINK_STATE_DOWN); 355a7ee7a7dSMarius Strobl sc->sc_flags &= ~LE_CARRIER; 356a7ee7a7dSMarius Strobl if (sc->sc_nocarrier) 357a7ee7a7dSMarius Strobl (*sc->sc_nocarrier)(sc); 358a7ee7a7dSMarius Strobl else 359a7ee7a7dSMarius Strobl if_printf(ifp, "lost carrier\n"); 360a7ee7a7dSMarius Strobl } 361a7ee7a7dSMarius Strobl if (tmd.tmd3 & LE_T3_LCOL) 362a7ee7a7dSMarius Strobl ifp->if_collisions++; 363a7ee7a7dSMarius Strobl if (tmd.tmd3 & LE_T3_RTRY) { 364a7ee7a7dSMarius Strobl #ifdef LEDEBUG 365a7ee7a7dSMarius Strobl if_printf(ifp, "excessive collisions, tdr %d\n", 366a7ee7a7dSMarius Strobl tmd.tmd3 & LE_T3_TDR_MASK); 367a7ee7a7dSMarius Strobl #endif 368a7ee7a7dSMarius Strobl ifp->if_collisions += 16; 369a7ee7a7dSMarius Strobl } 370a7ee7a7dSMarius Strobl ifp->if_oerrors++; 371a7ee7a7dSMarius Strobl } else { 372a7ee7a7dSMarius Strobl if (tmd.tmd1_bits & LE_T1_ONE) 373a7ee7a7dSMarius Strobl ifp->if_collisions++; 374a7ee7a7dSMarius Strobl else if (tmd.tmd1_bits & LE_T1_MORE) 375a7ee7a7dSMarius Strobl /* Real number is unknown. */ 376a7ee7a7dSMarius Strobl ifp->if_collisions += 2; 377a7ee7a7dSMarius Strobl ifp->if_opackets++; 378a7ee7a7dSMarius Strobl } 379a7ee7a7dSMarius Strobl 380a7ee7a7dSMarius Strobl if (++bix == sc->sc_ntbuf) 381a7ee7a7dSMarius Strobl bix = 0; 382a7ee7a7dSMarius Strobl 383a7ee7a7dSMarius Strobl --sc->sc_no_td; 384a7ee7a7dSMarius Strobl } 385a7ee7a7dSMarius Strobl 386a7ee7a7dSMarius Strobl sc->sc_first_td = bix; 387a7ee7a7dSMarius Strobl 3880c05ab41SMarius Strobl ifp->if_timer = sc->sc_no_td > 0 ? 5 : 0; 389a7ee7a7dSMarius Strobl } 390a7ee7a7dSMarius Strobl 391a7ee7a7dSMarius Strobl /* 392a7ee7a7dSMarius Strobl * Controller interrupt 393a7ee7a7dSMarius Strobl */ 394a7ee7a7dSMarius Strobl void 395a7ee7a7dSMarius Strobl am7990_intr(void *arg) 396a7ee7a7dSMarius Strobl { 397a7ee7a7dSMarius Strobl struct lance_softc *sc = arg; 398a7ee7a7dSMarius Strobl struct ifnet *ifp = sc->sc_ifp; 399a7ee7a7dSMarius Strobl uint16_t isr; 400a7ee7a7dSMarius Strobl 401a7ee7a7dSMarius Strobl LE_LOCK(sc); 402a7ee7a7dSMarius Strobl 403a7ee7a7dSMarius Strobl if (sc->sc_hwintr && (*sc->sc_hwintr)(sc) == -1) { 404a7ee7a7dSMarius Strobl ifp->if_ierrors++; 405a7ee7a7dSMarius Strobl lance_init_locked(sc); 406a7ee7a7dSMarius Strobl LE_UNLOCK(sc); 407a7ee7a7dSMarius Strobl return; 408a7ee7a7dSMarius Strobl } 409a7ee7a7dSMarius Strobl 410a7ee7a7dSMarius Strobl isr = (*sc->sc_rdcsr)(sc, LE_CSR0); 411a7ee7a7dSMarius Strobl #if defined(LEDEBUG) && LEDEBUG > 1 412a7ee7a7dSMarius Strobl if (sc->sc_flags & LE_DEBUG) 413a7ee7a7dSMarius Strobl if_printf(ifp, "%s: entering with isr=%04x\n", __func__, isr); 414a7ee7a7dSMarius Strobl #endif 415a7ee7a7dSMarius Strobl if ((isr & LE_C0_INTR) == 0) { 416a7ee7a7dSMarius Strobl LE_UNLOCK(sc); 417a7ee7a7dSMarius Strobl return; 418a7ee7a7dSMarius Strobl } 419a7ee7a7dSMarius Strobl 4200c05ab41SMarius Strobl /* 4210c05ab41SMarius Strobl * Clear interrupt source flags and turn off interrupts. If we 4220c05ab41SMarius Strobl * don't clear these flags before processing their sources we 42360c430f5SMarius Strobl * could completely miss some interrupt events as the NIC can 42460c430f5SMarius Strobl * change these flags while we're in this handler. We turn off 42560c430f5SMarius Strobl * interrupts so we don't get another RX interrupt while still 42660c430f5SMarius Strobl * processing the previous one in ifp->if_input() with the 42760c430f5SMarius Strobl * driver lock dropped. 4280c05ab41SMarius Strobl */ 4290c05ab41SMarius Strobl (*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD | 4300c05ab41SMarius Strobl LE_C0_STOP | LE_C0_STRT | LE_C0_INIT)); 4310c05ab41SMarius Strobl 432a7ee7a7dSMarius Strobl if (isr & LE_C0_ERR) { 433a7ee7a7dSMarius Strobl if (isr & LE_C0_BABL) { 434a7ee7a7dSMarius Strobl #ifdef LEDEBUG 435a7ee7a7dSMarius Strobl if_printf(ifp, "babble\n"); 436a7ee7a7dSMarius Strobl #endif 437a7ee7a7dSMarius Strobl ifp->if_oerrors++; 438a7ee7a7dSMarius Strobl } 439a7ee7a7dSMarius Strobl #if 0 440a7ee7a7dSMarius Strobl if (isr & LE_C0_CERR) { 441a7ee7a7dSMarius Strobl if_printf(ifp, "collision error\n"); 442a7ee7a7dSMarius Strobl ifp->if_collisions++; 443a7ee7a7dSMarius Strobl } 444a7ee7a7dSMarius Strobl #endif 445a7ee7a7dSMarius Strobl if (isr & LE_C0_MISS) { 446a7ee7a7dSMarius Strobl #ifdef LEDEBUG 447a7ee7a7dSMarius Strobl if_printf(ifp, "missed packet\n"); 448a7ee7a7dSMarius Strobl #endif 449a7ee7a7dSMarius Strobl ifp->if_ierrors++; 450a7ee7a7dSMarius Strobl } 451a7ee7a7dSMarius Strobl if (isr & LE_C0_MERR) { 452a7ee7a7dSMarius Strobl if_printf(ifp, "memory error\n"); 453a7ee7a7dSMarius Strobl lance_init_locked(sc); 454a7ee7a7dSMarius Strobl LE_UNLOCK(sc); 455a7ee7a7dSMarius Strobl return; 456a7ee7a7dSMarius Strobl } 457a7ee7a7dSMarius Strobl } 458a7ee7a7dSMarius Strobl 459a7ee7a7dSMarius Strobl if ((isr & LE_C0_RXON) == 0) { 460a7ee7a7dSMarius Strobl if_printf(ifp, "receiver disabled\n"); 461a7ee7a7dSMarius Strobl ifp->if_ierrors++; 462a7ee7a7dSMarius Strobl lance_init_locked(sc); 463a7ee7a7dSMarius Strobl LE_UNLOCK(sc); 464a7ee7a7dSMarius Strobl return; 465a7ee7a7dSMarius Strobl } 466a7ee7a7dSMarius Strobl if ((isr & LE_C0_TXON) == 0) { 467a7ee7a7dSMarius Strobl if_printf(ifp, "transmitter disabled\n"); 468a7ee7a7dSMarius Strobl ifp->if_oerrors++; 469a7ee7a7dSMarius Strobl lance_init_locked(sc); 470a7ee7a7dSMarius Strobl LE_UNLOCK(sc); 471a7ee7a7dSMarius Strobl return; 472a7ee7a7dSMarius Strobl } 473a7ee7a7dSMarius Strobl 474a7ee7a7dSMarius Strobl /* 475a7ee7a7dSMarius Strobl * Pretend we have carrier; if we don't this will be cleared shortly. 476a7ee7a7dSMarius Strobl */ 477a7ee7a7dSMarius Strobl if (!(sc->sc_flags & LE_CARRIER)) 478a7ee7a7dSMarius Strobl if_link_state_change(ifp, LINK_STATE_UP); 479a7ee7a7dSMarius Strobl sc->sc_flags |= LE_CARRIER; 480a7ee7a7dSMarius Strobl 481a7ee7a7dSMarius Strobl if (isr & LE_C0_RINT) 482a7ee7a7dSMarius Strobl am7990_rint(sc); 483a7ee7a7dSMarius Strobl if (isr & LE_C0_TINT) 484a7ee7a7dSMarius Strobl am7990_tint(sc); 485a7ee7a7dSMarius Strobl 4860c05ab41SMarius Strobl /* Enable interrupts again. */ 4870c05ab41SMarius Strobl (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA); 4880c05ab41SMarius Strobl 4890c05ab41SMarius Strobl if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 4900c05ab41SMarius Strobl am7990_start_locked(sc); 491a7ee7a7dSMarius Strobl 492a7ee7a7dSMarius Strobl LE_UNLOCK(sc); 493a7ee7a7dSMarius Strobl } 494a7ee7a7dSMarius Strobl 495a7ee7a7dSMarius Strobl /* 496a7ee7a7dSMarius Strobl * Set up output on interface. 497a7ee7a7dSMarius Strobl * Get another datagram to send off of the interface queue, and map it to the 498a7ee7a7dSMarius Strobl * interface before starting the output. 499a7ee7a7dSMarius Strobl */ 500a7ee7a7dSMarius Strobl static void 501a7ee7a7dSMarius Strobl am7990_start_locked(struct lance_softc *sc) 502a7ee7a7dSMarius Strobl { 503a7ee7a7dSMarius Strobl struct ifnet *ifp = sc->sc_ifp; 504a7ee7a7dSMarius Strobl struct letmd tmd; 505a7ee7a7dSMarius Strobl struct mbuf *m; 5060c05ab41SMarius Strobl int bix, enq, len, rp; 507a7ee7a7dSMarius Strobl 508a7ee7a7dSMarius Strobl LE_LOCK_ASSERT(sc, MA_OWNED); 509a7ee7a7dSMarius Strobl 510a7ee7a7dSMarius Strobl if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 511a7ee7a7dSMarius Strobl IFF_DRV_RUNNING) 512a7ee7a7dSMarius Strobl return; 513a7ee7a7dSMarius Strobl 514a7ee7a7dSMarius Strobl bix = sc->sc_last_td; 5150c05ab41SMarius Strobl enq = 0; 516a7ee7a7dSMarius Strobl 517a7ee7a7dSMarius Strobl for (; sc->sc_no_td < sc->sc_ntbuf && 518a7ee7a7dSMarius Strobl !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) { 519a7ee7a7dSMarius Strobl rp = LE_TMDADDR(sc, bix); 520a7ee7a7dSMarius Strobl (*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd)); 521a7ee7a7dSMarius Strobl 522a7ee7a7dSMarius Strobl if (tmd.tmd1_bits & LE_T1_OWN) { 523a7ee7a7dSMarius Strobl ifp->if_drv_flags |= IFF_DRV_OACTIVE; 524a7ee7a7dSMarius Strobl if_printf(ifp, 525a7ee7a7dSMarius Strobl "missing buffer, no_td = %d, last_td = %d\n", 526a7ee7a7dSMarius Strobl sc->sc_no_td, sc->sc_last_td); 527a7ee7a7dSMarius Strobl } 528a7ee7a7dSMarius Strobl 529a7ee7a7dSMarius Strobl IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 530a7ee7a7dSMarius Strobl if (m == 0) 531a7ee7a7dSMarius Strobl break; 532a7ee7a7dSMarius Strobl 533a7ee7a7dSMarius Strobl /* 534a7ee7a7dSMarius Strobl * If BPF is listening on this interface, let it see the packet 535a7ee7a7dSMarius Strobl * before we commit it to the wire. 536a7ee7a7dSMarius Strobl */ 537a7ee7a7dSMarius Strobl BPF_MTAP(ifp, m); 538a7ee7a7dSMarius Strobl 539a7ee7a7dSMarius Strobl /* 540a7ee7a7dSMarius Strobl * Copy the mbuf chain into the transmit buffer. 541a7ee7a7dSMarius Strobl */ 542a7ee7a7dSMarius Strobl len = lance_put(sc, LE_TBUFADDR(sc, bix), m); 543a7ee7a7dSMarius Strobl 544a7ee7a7dSMarius Strobl #ifdef LEDEBUG 545a7ee7a7dSMarius Strobl if (len > ETHERMTU + ETHER_HDR_LEN) 546a7ee7a7dSMarius Strobl if_printf(ifp, "packet length %d\n", len); 547a7ee7a7dSMarius Strobl #endif 548a7ee7a7dSMarius Strobl 549a7ee7a7dSMarius Strobl /* 550a7ee7a7dSMarius Strobl * Init transmit registers, and set transmit start flag. 551a7ee7a7dSMarius Strobl */ 552a7ee7a7dSMarius Strobl tmd.tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP; 553a7ee7a7dSMarius Strobl tmd.tmd2 = -len | LE_XMD2_ONES; 554a7ee7a7dSMarius Strobl tmd.tmd3 = 0; 555a7ee7a7dSMarius Strobl 556a7ee7a7dSMarius Strobl (*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd)); 557a7ee7a7dSMarius Strobl 558a7ee7a7dSMarius Strobl #ifdef LEDEBUG 559a7ee7a7dSMarius Strobl if (sc->sc_flags & LE_DEBUG) 56060c430f5SMarius Strobl am7990_xmit_print(sc, bix); 561a7ee7a7dSMarius Strobl #endif 562a7ee7a7dSMarius Strobl 563a7ee7a7dSMarius Strobl (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD); 5640c05ab41SMarius Strobl enq++; 565a7ee7a7dSMarius Strobl 566a7ee7a7dSMarius Strobl if (++bix == sc->sc_ntbuf) 567a7ee7a7dSMarius Strobl bix = 0; 568a7ee7a7dSMarius Strobl 569a7ee7a7dSMarius Strobl if (++sc->sc_no_td == sc->sc_ntbuf) { 570a7ee7a7dSMarius Strobl ifp->if_drv_flags |= IFF_DRV_OACTIVE; 571a7ee7a7dSMarius Strobl break; 572a7ee7a7dSMarius Strobl } 573a7ee7a7dSMarius Strobl } 574a7ee7a7dSMarius Strobl 575a7ee7a7dSMarius Strobl sc->sc_last_td = bix; 5760c05ab41SMarius Strobl 5770c05ab41SMarius Strobl if (enq > 0) 5780c05ab41SMarius Strobl ifp->if_timer = 5; 579a7ee7a7dSMarius Strobl } 580a7ee7a7dSMarius Strobl 581a7ee7a7dSMarius Strobl #ifdef LEDEBUG 582a7ee7a7dSMarius Strobl static void 583a7ee7a7dSMarius Strobl am7990_recv_print(struct lance_softc *sc, int no) 584a7ee7a7dSMarius Strobl { 585a7ee7a7dSMarius Strobl struct ifnet *ifp = sc->sc_ifp; 586a7ee7a7dSMarius Strobl struct ether_header eh; 587a7ee7a7dSMarius Strobl struct lermd rmd; 588a7ee7a7dSMarius Strobl uint16_t len; 589a7ee7a7dSMarius Strobl 590a7ee7a7dSMarius Strobl (*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd)); 591a7ee7a7dSMarius Strobl len = rmd.rmd3; 592a7ee7a7dSMarius Strobl if_printf(ifp, "receive buffer %d, len = %d\n", no, len); 593a7ee7a7dSMarius Strobl if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0)); 594a7ee7a7dSMarius Strobl if_printf(ifp, 595a7ee7a7dSMarius Strobl "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n", 596a7ee7a7dSMarius Strobl rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits, rmd.rmd2, rmd.rmd3); 597a7ee7a7dSMarius Strobl if (len - ETHER_CRC_LEN >= sizeof(eh)) { 598a7ee7a7dSMarius Strobl (*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh)); 599a7ee7a7dSMarius Strobl if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost)); 600a7ee7a7dSMarius Strobl printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost), 601a7ee7a7dSMarius Strobl ntohs(eh.ether_type)); 602a7ee7a7dSMarius Strobl } 603a7ee7a7dSMarius Strobl } 604a7ee7a7dSMarius Strobl 605a7ee7a7dSMarius Strobl static void 606a7ee7a7dSMarius Strobl am7990_xmit_print(struct lance_softc *sc, int no) 607a7ee7a7dSMarius Strobl { 608a7ee7a7dSMarius Strobl struct ifnet *ifp = sc->sc_ifp; 609a7ee7a7dSMarius Strobl struct ether_header eh; 610a7ee7a7dSMarius Strobl struct letmd tmd; 611a7ee7a7dSMarius Strobl uint16_t len; 612a7ee7a7dSMarius Strobl 613a7ee7a7dSMarius Strobl (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd)); 614a7ee7a7dSMarius Strobl len = -tmd.tmd2; 615a7ee7a7dSMarius Strobl if_printf(ifp, "transmit buffer %d, len = %d\n", no, len); 616a7ee7a7dSMarius Strobl if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0)); 617a7ee7a7dSMarius Strobl if_printf(ifp, 618a7ee7a7dSMarius Strobl "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n", 619a7ee7a7dSMarius Strobl tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, tmd.tmd2, tmd.tmd3); 620a7ee7a7dSMarius Strobl if (len >= sizeof(eh)) { 621a7ee7a7dSMarius Strobl (*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh)); 622a7ee7a7dSMarius Strobl if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost)); 623a7ee7a7dSMarius Strobl printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost), 624a7ee7a7dSMarius Strobl ntohs(eh.ether_type)); 625a7ee7a7dSMarius Strobl } 626a7ee7a7dSMarius Strobl } 627a7ee7a7dSMarius Strobl #endif /* LEDEBUG */ 628