166550Snyan/* $FreeBSD$ */ 266550Snyan/* $NecBSD: dp83932.c,v 1.5 1999/07/29 05:08:44 kmatsuda Exp $ */ 366550Snyan/* $NetBSD: if_snc.c,v 1.18 1998/04/25 21:27:40 scottr Exp $ */ 466550Snyan 5139749Simp/*- 666550Snyan * Copyright (c) 1997, 1998, 1999 766550Snyan * Kouichi Matsuda. All rights reserved. 866550Snyan * 966550Snyan * Redistribution and use in source and binary forms, with or without 1066550Snyan * modification, are permitted provided that the following conditions 1166550Snyan * are met: 1266550Snyan * 1. Redistributions of source code must retain the above copyright 1366550Snyan * notice, this list of conditions and the following disclaimer. 1466550Snyan * 2. Redistributions in binary form must reproduce the above copyright 1566550Snyan * notice, this list of conditions and the following disclaimer in the 1666550Snyan * documentation and/or other materials provided with the distribution. 1766550Snyan * 3. All advertising materials mentioning features or use of this software 1866550Snyan * must display the following acknowledgement: 1966550Snyan * This product includes software developed by Kouichi Matsuda for 2066550Snyan * NetBSD/pc98. 2166550Snyan * 4. The name of the author may not be used to endorse or promote products 2266550Snyan * derived from this software without specific prior written permission 2366550Snyan * 2466550Snyan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2566550Snyan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2666550Snyan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2766550Snyan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2866550Snyan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2966550Snyan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3066550Snyan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3166550Snyan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3266550Snyan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3366550Snyan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3466550Snyan */ 3566550Snyan 3666550Snyan/* 3766550Snyan * Modified for FreeBSD(98) 4.0 from NetBSD/pc98 1.4.2 by Motomichi Matsuzaki. 3866550Snyan */ 3966550Snyan 4066550Snyan/* 4166550Snyan * Modified for NetBSD/pc98 1.2G from NetBSD/mac68k 1.2G by Kouichi Matsuda. 4266550Snyan * Make adapted for NEC PC-9801-83, 84, PC-9801-103, 104, PC-9801N-25 and 4366550Snyan * PC-9801N-J02, J02R, which uses National Semiconductor DP83934AVQB as 4466550Snyan * Ethernet Controller and National Semiconductor NS46C46 as 4566550Snyan * (64 * 16 bits) Microwire Serial EEPROM. 4666550Snyan */ 4766550Snyan 48139749Simp/*- 4966550Snyan * National Semiconductor DP8393X SONIC Driver 5066550Snyan * Copyright (c) 1991 Algorithmics Ltd (http://www.algor.co.uk) 5166550Snyan * You may use, copy, and modify this program so long as you retain the 5266550Snyan * copyright line. 5366550Snyan * 5466550Snyan * This driver has been substantially modified since Algorithmics donated 5566550Snyan * it. 5666550Snyan * 5766550Snyan * Denton Gentry <denny1@home.com> 5866550Snyan * and also 5966550Snyan * Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp> 6066550Snyan * did the work to get this running on the Macintosh. 6166550Snyan */ 6266550Snyan 6366550Snyan#include "opt_inet.h" 6466550Snyan 6566550Snyan#include <sys/param.h> 66181298Sjhb#include <sys/kernel.h> 6766550Snyan#include <sys/systm.h> 6866550Snyan#include <sys/sockio.h> 6966550Snyan#include <sys/mbuf.h> 7066550Snyan#include <sys/protosw.h> 7166550Snyan#include <sys/socket.h> 7266550Snyan#include <sys/syslog.h> 7366550Snyan#include <sys/errno.h> 7466550Snyan 7566550Snyan#include <net/ethernet.h> 7666550Snyan#include <net/if.h> 7766550Snyan#include <net/if_arp.h> 7866550Snyan#include <net/if_dl.h> 7966550Snyan#include <net/if_media.h> 80147256Sbrooks#include <net/if_types.h> 8166550Snyan 8266550Snyan#include <net/bpf.h> 8366550Snyan 8466550Snyan#include <sys/bus.h> 8566550Snyan#include <machine/bus.h> 8666550Snyan#include <dev/snc/dp83932reg.h> 8766550Snyan#include <dev/snc/dp83932var.h> 8866550Snyan 89181298Sjhbstatic void sncwatchdog(void *); 90179442Sjhbstatic void sncinit(void *); 91181298Sjhbstatic void sncinit_locked(struct snc_softc *); 92179442Sjhbstatic int sncstop(struct snc_softc *sc); 93179442Sjhbstatic int sncioctl(struct ifnet *ifp, u_long cmd, caddr_t data); 94179442Sjhbstatic void sncstart(struct ifnet *ifp); 95181298Sjhbstatic void sncstart_locked(struct ifnet *ifp); 96179442Sjhbstatic void sncreset(struct snc_softc *sc); 9766550Snyan 98179442Sjhbstatic void caminitialise(struct snc_softc *); 99179442Sjhbstatic void camentry(struct snc_softc *, int, u_char *ea); 100179442Sjhbstatic void camprogram(struct snc_softc *); 101179442Sjhbstatic void initialise_tda(struct snc_softc *); 102179442Sjhbstatic void initialise_rda(struct snc_softc *); 103179442Sjhbstatic void initialise_rra(struct snc_softc *); 10466550Snyan#ifdef SNCDEBUG 105179442Sjhbstatic void camdump(struct snc_softc *sc); 10666550Snyan#endif 10766550Snyan 108179442Sjhbstatic void sonictxint(struct snc_softc *); 109179442Sjhbstatic void sonicrxint(struct snc_softc *); 11066550Snyan 111179442Sjhbstatic u_int sonicput(struct snc_softc *sc, struct mbuf *m0, int mtd_next); 112179442Sjhbstatic int sonic_read(struct snc_softc *, u_int32_t, int); 113179442Sjhbstatic struct mbuf *sonic_get(struct snc_softc *, u_int32_t, int); 11466550Snyan 11592739Salfredint snc_enable(struct snc_softc *); 11692739Salfredvoid snc_disable(struct snc_softc *); 11766550Snyan 11892739Salfredint snc_mediachange(struct ifnet *); 11992739Salfredvoid snc_mediastatus(struct ifnet *, struct ifmediareq *); 12066550Snyan 12166550Snyan#undef assert 12266550Snyan#undef _assert 12366550Snyan 12466550Snyan#ifdef NDEBUG 12566550Snyan#define assert(e) ((void)0) 12666550Snyan#define _assert(e) ((void)0) 12766550Snyan#else 12866550Snyan#define _assert(e) assert(e) 12966550Snyan#ifdef __STDC__ 13066550Snyan#define assert(e) ((e) ? (void)0 : __assert("snc ", __FILE__, __LINE__, #e)) 13166550Snyan#else /* PCC */ 13266550Snyan#define assert(e) ((e) ? (void)0 : __assert("snc "__FILE__, __LINE__, "e")) 13366550Snyan#endif 13466550Snyan#endif 13566550Snyan 13666550Snyan#ifdef SNCDEBUG 13766550Snyan#define SNC_SHOWTXHDR 0x01 /* show tx ether_header */ 13866550Snyan#define SNC_SHOWRXHDR 0x02 /* show rx ether_header */ 13966550Snyan#define SNC_SHOWCAMENT 0x04 /* show CAM entry */ 14066550Snyan#endif /* SNCDEBUG */ 14166550Snyanint sncdebug = 0; 14266550Snyan 14366550Snyan 144181298Sjhbint 145243455Snyansncconfig(struct snc_softc *sc, int *media, int nmedia, int defmedia, 146243455Snyan u_int8_t *myea) 14766550Snyan{ 148147256Sbrooks struct ifnet *ifp; 14966550Snyan int i; 15066550Snyan 15166550Snyan#ifdef SNCDEBUG 15266550Snyan if ((sncdebug & SNC_SHOWCAMENT) != 0) { 15366550Snyan camdump(sc); 15466550Snyan } 15566550Snyan#endif 15666550Snyan 157147256Sbrooks ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 158181298Sjhb if (ifp == NULL) { 159181298Sjhb device_printf(sc->sc_dev, "can not if_alloc()\n"); 160181298Sjhb return (ENOMEM); 161181298Sjhb } 162147256Sbrooks 16366550Snyan#ifdef SNCDEBUG 16466550Snyan device_printf(sc->sc_dev, 16566550Snyan "buffers: rra=0x%x cda=0x%x rda=0x%x tda=0x%x\n", 16666550Snyan sc->v_rra[0], sc->v_cda, 16766550Snyan sc->v_rda, sc->mtda[0].mtd_vtxp); 16866550Snyan#endif 16966550Snyan 17066550Snyan ifp->if_softc = sc; 171121867Sbrooks if_initname(ifp, device_get_name(sc->sc_dev), 172121867Sbrooks device_get_unit(sc->sc_dev)); 17366550Snyan ifp->if_ioctl = sncioctl; 17466550Snyan ifp->if_start = sncstart; 175181298Sjhb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 17666550Snyan ifp->if_init = sncinit; 17766550Snyan ifp->if_mtu = ETHERMTU; 178207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 17966550Snyan 18066550Snyan /* Initialize media goo. */ 18166550Snyan ifmedia_init(&sc->sc_media, 0, snc_mediachange, 18266550Snyan snc_mediastatus); 18366550Snyan if (media != NULL) { 18466550Snyan for (i = 0; i < nmedia; i++) 18566550Snyan ifmedia_add(&sc->sc_media, media[i], 0, NULL); 18666550Snyan ifmedia_set(&sc->sc_media, defmedia); 18766550Snyan } else { 18866550Snyan ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL); 18966550Snyan ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL); 19066550Snyan } 19166550Snyan 192106937Ssam ether_ifattach(ifp, myea); 193181298Sjhb return (0); 19466550Snyan} 19566550Snyan 19666550Snyanvoid 197243455Snyansncshutdown(void *arg) 19866550Snyan{ 199181298Sjhb struct snc_softc *sc = arg; 20066550Snyan 201181298Sjhb SNC_ASSERT_LOCKED(sc); 202181298Sjhb sncstop(sc); 20366550Snyan} 20466550Snyan 20566550Snyan/* 20666550Snyan * Media change callback. 20766550Snyan */ 20866550Snyanint 209243455Snyansnc_mediachange(struct ifnet *ifp) 21066550Snyan{ 21166550Snyan struct snc_softc *sc = ifp->if_softc; 212181298Sjhb int error; 21366550Snyan 214181298Sjhb SNC_LOCK(sc); 21566550Snyan if (sc->sc_mediachange) 216181298Sjhb error = (*sc->sc_mediachange)(sc); 217181298Sjhb else 218181298Sjhb error = EINVAL; 219181298Sjhb SNC_UNLOCK(sc); 220181298Sjhb return (error); 22166550Snyan} 22266550Snyan 22366550Snyan/* 22466550Snyan * Media status callback. 22566550Snyan */ 22666550Snyanvoid 227243455Snyansnc_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) 22866550Snyan{ 22966550Snyan struct snc_softc *sc = ifp->if_softc; 23066550Snyan 231181298Sjhb SNC_LOCK(sc); 23266550Snyan if (sc->sc_enabled == 0) { 23366550Snyan ifmr->ifm_active = IFM_ETHER | IFM_NONE; 23466550Snyan ifmr->ifm_status = 0; 235181298Sjhb SNC_UNLOCK(sc); 23666550Snyan return; 23766550Snyan } 23866550Snyan 23966550Snyan if (sc->sc_mediastatus) 24066550Snyan (*sc->sc_mediastatus)(sc, ifmr); 241181298Sjhb SNC_UNLOCK(sc); 24266550Snyan} 24366550Snyan 24466550Snyan 245179442Sjhbstatic int 246243455Snyansncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 24766550Snyan{ 24866550Snyan struct ifreq *ifr; 24966550Snyan struct snc_softc *sc = ifp->if_softc; 250181298Sjhb int err = 0; 25166550Snyan 25266550Snyan switch (cmd) { 25366550Snyan 25466550Snyan case SIOCSIFFLAGS: 255181298Sjhb SNC_LOCK(sc); 25666550Snyan if ((ifp->if_flags & IFF_UP) == 0 && 257148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 25866550Snyan /* 25966550Snyan * If interface is marked down and it is running, 26066550Snyan * then stop it. 26166550Snyan */ 26266550Snyan sncstop(sc); 26366550Snyan snc_disable(sc); 26466550Snyan } else if ((ifp->if_flags & IFF_UP) != 0 && 265148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 26666550Snyan /* 26766550Snyan * If interface is marked up and it is stopped, 26866550Snyan * then start it. 26966550Snyan */ 27066550Snyan if ((err = snc_enable(sc)) != 0) 27166550Snyan break; 272181298Sjhb sncinit_locked(sc); 27366550Snyan } else if (sc->sc_enabled) { 27466550Snyan /* 27566550Snyan * reset the interface to pick up any other changes 27666550Snyan * in flags 27766550Snyan */ 27866550Snyan sncreset(sc); 279181298Sjhb sncstart_locked(ifp); 28066550Snyan } 281181298Sjhb SNC_UNLOCK(sc); 28266550Snyan break; 28366550Snyan 28466550Snyan case SIOCADDMULTI: 28566550Snyan case SIOCDELMULTI: 286181298Sjhb SNC_LOCK(sc); 28766550Snyan if (sc->sc_enabled == 0) { 28866550Snyan err = EIO; 289181298Sjhb SNC_UNLOCK(sc); 29066550Snyan break; 29166550Snyan } 29266550Snyan sncreset(sc); 293181298Sjhb SNC_UNLOCK(sc); 29466550Snyan err = 0; 29566550Snyan break; 29666550Snyan case SIOCGIFMEDIA: 29766550Snyan case SIOCSIFMEDIA: 29866550Snyan ifr = (struct ifreq *) data; 29966550Snyan err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 30066550Snyan break; 30166550Snyan default: 302106937Ssam err = ether_ioctl(ifp, cmd, data); 303106937Ssam break; 30466550Snyan } 30566550Snyan return (err); 30666550Snyan} 30766550Snyan 30866550Snyan/* 30966550Snyan * Encapsulate a packet of type family for the local net. 31066550Snyan */ 311179442Sjhbstatic void 312243455Snyansncstart(struct ifnet *ifp) 31366550Snyan{ 31466550Snyan struct snc_softc *sc = ifp->if_softc; 315181298Sjhb 316181298Sjhb SNC_LOCK(sc); 317181298Sjhb sncstart_locked(ifp); 318181298Sjhb SNC_UNLOCK(sc); 319181298Sjhb} 320181298Sjhb 321181298Sjhbstatic void 322243455Snyansncstart_locked(struct ifnet *ifp) 323181298Sjhb{ 324181298Sjhb struct snc_softc *sc = ifp->if_softc; 32566550Snyan struct mbuf *m; 32666550Snyan int mtd_next; 32766550Snyan 328148887Srwatson if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 329148887Srwatson IFF_DRV_RUNNING) 33066550Snyan return; 33166550Snyan 33266550Snyanoutloop: 33366550Snyan /* Check for room in the xmit buffer. */ 33466550Snyan if ((mtd_next = (sc->mtd_free + 1)) == NTDA) 33566550Snyan mtd_next = 0; 33666550Snyan 33766550Snyan if (mtd_next == sc->mtd_hw) { 338148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 33966550Snyan return; 34066550Snyan } 34166550Snyan 34266550Snyan IF_DEQUEUE(&ifp->if_snd, m); 34366550Snyan if (m == 0) 34466550Snyan return; 34566550Snyan 34666550Snyan /* We need the header for m_pkthdr.len. */ 347113255Sdes M_ASSERTPKTHDR(m); 34866550Snyan 34966550Snyan /* 35066550Snyan * If there is nothing in the o/p queue, and there is room in 35166550Snyan * the Tx ring, then send the packet directly. Otherwise append 35266550Snyan * it to the o/p queue. 35366550Snyan */ 35466550Snyan if ((sonicput(sc, m, mtd_next)) == 0) { 35566550Snyan IF_PREPEND(&ifp->if_snd, m); 35666550Snyan return; 35766550Snyan } 35866550Snyan 359142180Smlaier /* 360142180Smlaier * If bpf is listening on this interface, let it see the packet 361142180Smlaier * before we commit it to the wire, but only if we are really 362142180Smlaier * committed to send it. 363142180Smlaier * 364142180Smlaier * XXX: Locking must protect m against premature m_freem() in 365142180Smlaier * sonictxint(). 366142180Smlaier */ 367142180Smlaier BPF_MTAP(ifp, m); 368142180Smlaier 36966550Snyan sc->mtd_prev = sc->mtd_free; 37066550Snyan sc->mtd_free = mtd_next; 37166550Snyan 37266550Snyan ifp->if_opackets++; /* # of pkts */ 37366550Snyan 37466550Snyan /* Jump back for possibly more punishment. */ 37566550Snyan goto outloop; 37666550Snyan} 37766550Snyan 37866550Snyan/* 37966550Snyan * reset and restart the SONIC. Called in case of fatal 38066550Snyan * hardware/software errors. 38166550Snyan */ 382179442Sjhbstatic void 383243455Snyansncreset(struct snc_softc *sc) 38466550Snyan{ 38566550Snyan sncstop(sc); 386181298Sjhb sncinit_locked(sc); 38766550Snyan} 38866550Snyan 389179442Sjhbstatic void 390243455Snyansncinit(void *xsc) 39166550Snyan{ 39266550Snyan struct snc_softc *sc = xsc; 393181298Sjhb 394181298Sjhb SNC_LOCK(sc); 395181298Sjhb sncinit_locked(sc); 396181298Sjhb SNC_UNLOCK(sc); 397181298Sjhb} 398181298Sjhb 399181298Sjhbstatic void 400181298Sjhbsncinit_locked(struct snc_softc *sc) 401181298Sjhb{ 40266550Snyan u_long s_rcr; 40366550Snyan 404148887Srwatson if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 40566550Snyan /* already running */ 40666550Snyan return; 40766550Snyan 40866550Snyan NIC_PUT(sc, SNCR_CR, CR_RST); /* DCR only accessable in reset mode! */ 40966550Snyan 41066550Snyan /* config it */ 41166550Snyan NIC_PUT(sc, SNCR_DCR, (sc->sncr_dcr | 41266550Snyan (sc->bitmode ? DCR_DW32 : DCR_DW16))); 41366550Snyan NIC_PUT(sc, SNCR_DCR2, sc->sncr_dcr2); 41466550Snyan 41566550Snyan s_rcr = RCR_BRD | RCR_LBNONE; 416147256Sbrooks if (sc->sc_ifp->if_flags & IFF_PROMISC) 41766550Snyan s_rcr |= RCR_PRO; 418147256Sbrooks if (sc->sc_ifp->if_flags & IFF_ALLMULTI) 41966550Snyan s_rcr |= RCR_AMC; 42066550Snyan NIC_PUT(sc, SNCR_RCR, s_rcr); 42166550Snyan 42266550Snyan NIC_PUT(sc, SNCR_IMR, (IMR_PRXEN | IMR_PTXEN | IMR_TXEREN | IMR_LCDEN)); 42366550Snyan 42466550Snyan /* clear pending interrupts */ 42566550Snyan NIC_PUT(sc, SNCR_ISR, ISR_ALL); 42666550Snyan 42766550Snyan /* clear tally counters */ 42866550Snyan NIC_PUT(sc, SNCR_CRCT, -1); 42966550Snyan NIC_PUT(sc, SNCR_FAET, -1); 43066550Snyan NIC_PUT(sc, SNCR_MPT, -1); 43166550Snyan 43266550Snyan initialise_tda(sc); 43366550Snyan initialise_rda(sc); 43466550Snyan initialise_rra(sc); 43566550Snyan 43666550Snyan /* enable the chip */ 43766550Snyan NIC_PUT(sc, SNCR_CR, 0); 43866550Snyan wbflush(); 43966550Snyan 44066550Snyan /* program the CAM */ 44166550Snyan camprogram(sc); 44266550Snyan 44366550Snyan /* get it to read resource descriptors */ 44466550Snyan NIC_PUT(sc, SNCR_CR, CR_RRRA); 44566550Snyan wbflush(); 44666550Snyan while ((NIC_GET(sc, SNCR_CR)) & CR_RRRA) 44766550Snyan continue; 44866550Snyan 44966550Snyan /* enable rx */ 45066550Snyan NIC_PUT(sc, SNCR_CR, CR_RXEN); 45166550Snyan wbflush(); 45266550Snyan 45366550Snyan /* flag interface as "running" */ 454148887Srwatson sc->sc_ifp->if_drv_flags |= IFF_DRV_RUNNING; 455148887Srwatson sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 456181298Sjhb callout_reset(&sc->sc_timer, hz, sncwatchdog, sc); 45766550Snyan 45866550Snyan return; 45966550Snyan} 46066550Snyan 46166550Snyan/* 46266550Snyan * close down an interface and free its buffers 46366550Snyan * Called on final close of device, or if sncinit() fails 46466550Snyan * part way through. 46566550Snyan */ 466179442Sjhbstatic int 467243455Snyansncstop(struct snc_softc *sc) 46866550Snyan{ 46966550Snyan struct mtd *mtd; 47066550Snyan 471181298Sjhb SNC_ASSERT_LOCKED(sc); 472181298Sjhb 47366550Snyan /* stick chip in reset */ 47466550Snyan NIC_PUT(sc, SNCR_CR, CR_RST); 47566550Snyan wbflush(); 47666550Snyan 47766550Snyan /* free all receive buffers (currently static so nothing to do) */ 47866550Snyan 47966550Snyan /* free all pending transmit mbufs */ 48066550Snyan while (sc->mtd_hw != sc->mtd_free) { 48166550Snyan mtd = &sc->mtda[sc->mtd_hw]; 48266550Snyan if (mtd->mtd_mbuf) 48366550Snyan m_freem(mtd->mtd_mbuf); 48466550Snyan if (++sc->mtd_hw == NTDA) sc->mtd_hw = 0; 48566550Snyan } 48666550Snyan 487181298Sjhb callout_stop(&sc->sc_timer); 488181298Sjhb sc->sc_tx_timeout = 0; 489181298Sjhb sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 49066550Snyan 49166550Snyan return (0); 49266550Snyan} 49366550Snyan 49466550Snyan/* 49566550Snyan * Called if any Tx packets remain unsent after 5 seconds, 49666550Snyan * In all cases we just reset the chip, and any retransmission 49766550Snyan * will be handled by higher level protocol timeouts. 49866550Snyan */ 499179442Sjhbstatic void 500181298Sjhbsncwatchdog(void *arg) 50166550Snyan{ 502181298Sjhb struct snc_softc *sc = arg; 50366550Snyan struct mtd *mtd; 50466550Snyan 505181298Sjhb SNC_ASSERT_LOCKED(sc); 506181298Sjhb if (sc->sc_tx_timeout && --sc->sc_tx_timeout == 0) { 507181298Sjhb if (sc->mtd_hw != sc->mtd_free) { 508181298Sjhb /* something still pending for transmit */ 509181298Sjhb mtd = &sc->mtda[sc->mtd_hw]; 510181298Sjhb if (SRO(sc, mtd->mtd_vtxp, TXP_STATUS) == 0) 511181298Sjhb log(LOG_ERR, "%s: Tx - timeout\n", 512181298Sjhb device_get_nameunit(sc->sc_dev)); 513181298Sjhb else 514181298Sjhb log(LOG_ERR, "%s: Tx - lost interrupt\n", 515181298Sjhb device_get_nameunit(sc->sc_dev)); 516181298Sjhb sncreset(sc); 517181298Sjhb } 51866550Snyan } 519181298Sjhb callout_reset(&sc->sc_timer, hz, sncwatchdog, sc); 52066550Snyan} 52166550Snyan 52266550Snyan/* 523181298Sjhb * stuff packet into sonic 52466550Snyan */ 525179442Sjhbstatic u_int 526243455Snyansonicput(struct snc_softc *sc, struct mbuf *m0, int mtd_next) 52766550Snyan{ 52866550Snyan struct mtd *mtdp; 52966550Snyan struct mbuf *m; 53066550Snyan u_int32_t buff; 53166550Snyan u_int32_t txp; 53266550Snyan u_int len = 0; 53366550Snyan u_int totlen = 0; 53466550Snyan 53566550Snyan#ifdef whyonearthwouldyoudothis 53666550Snyan if (NIC_GET(sc, SNCR_CR) & CR_TXP) 53766550Snyan return (0); 53866550Snyan#endif 53966550Snyan 54066550Snyan /* grab the replacement mtd */ 54166550Snyan mtdp = &sc->mtda[sc->mtd_free]; 54266550Snyan 54366550Snyan buff = mtdp->mtd_vbuf; 54466550Snyan 54566550Snyan /* this packet goes to mtdnext fill in the TDA */ 54666550Snyan mtdp->mtd_mbuf = m0; 54766550Snyan txp = mtdp->mtd_vtxp; 54866550Snyan 54966550Snyan /* Write to the config word. Every (NTDA/2)+1 packets we set an intr */ 55066550Snyan if (sc->mtd_pint == 0) { 55166550Snyan sc->mtd_pint = NTDA/2; 55266550Snyan SWO(sc, txp, TXP_CONFIG, TCR_PINT); 55366550Snyan } else { 55466550Snyan sc->mtd_pint--; 55566550Snyan SWO(sc, txp, TXP_CONFIG, 0); 55666550Snyan } 55766550Snyan 55866550Snyan for (m = m0; m; m = m->m_next) { 55966550Snyan len = m->m_len; 56066550Snyan totlen += len; 56166550Snyan (*sc->sc_copytobuf)(sc, mtod(m, caddr_t), buff, len); 56266550Snyan buff += len; 56366550Snyan } 56466550Snyan if (totlen >= TXBSIZE) { 56566550Snyan panic("%s: sonicput: packet overflow", 56666550Snyan device_get_nameunit(sc->sc_dev)); 56766550Snyan } 56866550Snyan 56966550Snyan SWO(sc, txp, TXP_FRAGOFF + (0 * TXP_FRAGSIZE) + TXP_FPTRLO, 57066550Snyan LOWER(mtdp->mtd_vbuf)); 57166550Snyan SWO(sc, txp, TXP_FRAGOFF + (0 * TXP_FRAGSIZE) + TXP_FPTRHI, 57266550Snyan UPPER(mtdp->mtd_vbuf)); 57366550Snyan 57466550Snyan if (totlen < ETHERMIN + sizeof(struct ether_header)) { 57566550Snyan int pad = ETHERMIN + sizeof(struct ether_header) - totlen; 57666550Snyan (*sc->sc_zerobuf)(sc, mtdp->mtd_vbuf + totlen, pad); 57766550Snyan totlen = ETHERMIN + sizeof(struct ether_header); 57866550Snyan } 57966550Snyan 58066550Snyan SWO(sc, txp, TXP_FRAGOFF + (0 * TXP_FRAGSIZE) + TXP_FSIZE, 58166550Snyan totlen); 58266550Snyan SWO(sc, txp, TXP_FRAGCNT, 1); 58366550Snyan SWO(sc, txp, TXP_PKTSIZE, totlen); 58466550Snyan 58566550Snyan /* link onto the next mtd that will be used */ 58666550Snyan SWO(sc, txp, TXP_FRAGOFF + (1 * TXP_FRAGSIZE) + TXP_FPTRLO, 58766550Snyan LOWER(sc->mtda[mtd_next].mtd_vtxp) | EOL); 58866550Snyan 58966550Snyan /* 59066550Snyan * The previous txp.tlink currently contains a pointer to 59166550Snyan * our txp | EOL. Want to clear the EOL, so write our 59266550Snyan * pointer to the previous txp. 59366550Snyan */ 59466550Snyan SWO(sc, sc->mtda[sc->mtd_prev].mtd_vtxp, sc->mtd_tlinko, 59566550Snyan LOWER(mtdp->mtd_vtxp)); 59666550Snyan 59766550Snyan /* make sure chip is running */ 59866550Snyan wbflush(); 59966550Snyan NIC_PUT(sc, SNCR_CR, CR_TXP); 60066550Snyan wbflush(); 60166550Snyan 602181298Sjhb /* 5 seconds to watch for failing to transmit */ 603181298Sjhb sc->sc_tx_timeout = 5; 604181298Sjhb 60566550Snyan return (totlen); 60666550Snyan} 60766550Snyan 60866550Snyan/* 60966550Snyan * These are called from sonicioctl() when /etc/ifconfig is run to set 61066550Snyan * the address or switch the i/f on. 61166550Snyan */ 61266550Snyan/* 61366550Snyan * CAM support 61466550Snyan */ 615179442Sjhbstatic void 616243455Snyancaminitialise(struct snc_softc *sc) 61766550Snyan{ 61866550Snyan u_int32_t v_cda = sc->v_cda; 61966550Snyan int i; 62066550Snyan int camoffset; 62166550Snyan 62266550Snyan for (i = 0; i < MAXCAM; i++) { 62366550Snyan camoffset = i * CDA_CAMDESC; 62466550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMEP), i); 62566550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMAP2), 0); 62666550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMAP1), 0); 62766550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMAP0), 0); 62866550Snyan } 62966550Snyan SWO(sc, v_cda, CDA_ENABLE, 0); 63066550Snyan 63166550Snyan#ifdef SNCDEBUG 63266550Snyan if ((sncdebug & SNC_SHOWCAMENT) != 0) { 63366550Snyan camdump(sc); 63466550Snyan } 63566550Snyan#endif 63666550Snyan} 63766550Snyan 638179442Sjhbstatic void 639243455Snyancamentry(struct snc_softc *sc, int entry, u_char *ea) 64066550Snyan{ 64166550Snyan u_int32_t v_cda = sc->v_cda; 64266550Snyan int camoffset = entry * CDA_CAMDESC; 64366550Snyan 64466550Snyan SWO(sc, v_cda, camoffset + CDA_CAMEP, entry); 64566550Snyan SWO(sc, v_cda, camoffset + CDA_CAMAP2, (ea[5] << 8) | ea[4]); 64666550Snyan SWO(sc, v_cda, camoffset + CDA_CAMAP1, (ea[3] << 8) | ea[2]); 64766550Snyan SWO(sc, v_cda, camoffset + CDA_CAMAP0, (ea[1] << 8) | ea[0]); 64866550Snyan SWO(sc, v_cda, CDA_ENABLE, 64966550Snyan (SRO(sc, v_cda, CDA_ENABLE) | (1 << entry))); 65066550Snyan} 65166550Snyan 652179442Sjhbstatic void 653243455Snyancamprogram(struct snc_softc *sc) 65466550Snyan{ 65566550Snyan struct ifmultiaddr *ifma; 65666550Snyan struct ifnet *ifp; 65766550Snyan int timeout; 65866550Snyan int mcount = 0; 65966550Snyan 66066550Snyan caminitialise(sc); 66166550Snyan 662147256Sbrooks ifp = sc->sc_ifp; 66366550Snyan 66466550Snyan /* Always load our own address first. */ 665152315Sru camentry (sc, mcount, IF_LLADDR(sc->sc_ifp)); 66666550Snyan mcount++; 66766550Snyan 66866550Snyan /* Assume we won't need allmulti bit. */ 66966550Snyan ifp->if_flags &= ~IFF_ALLMULTI; 67066550Snyan 67166550Snyan /* Loop through multicast addresses */ 672195049Srwatson if_maddr_rlock(ifp); 67372084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 67466550Snyan if (ifma->ifma_addr->sa_family != AF_LINK) 67566550Snyan continue; 67666550Snyan if (mcount == MAXCAM) { 67766550Snyan ifp->if_flags |= IFF_ALLMULTI; 67866550Snyan break; 67966550Snyan } 68066550Snyan 68166550Snyan /* program the CAM with the specified entry */ 68266550Snyan camentry(sc, mcount, 68366550Snyan LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 68466550Snyan mcount++; 68566550Snyan } 686195049Srwatson if_maddr_runlock(ifp); 68766550Snyan 68866550Snyan NIC_PUT(sc, SNCR_CDP, LOWER(sc->v_cda)); 68966550Snyan NIC_PUT(sc, SNCR_CDC, MAXCAM); 69066550Snyan NIC_PUT(sc, SNCR_CR, CR_LCAM); 69166550Snyan wbflush(); 69266550Snyan 69366550Snyan timeout = 10000; 69466550Snyan while ((NIC_GET(sc, SNCR_CR) & CR_LCAM) && timeout--) 69566550Snyan continue; 69666550Snyan if (timeout == 0) { 69766550Snyan /* XXX */ 69866550Snyan panic("%s: CAM initialisation failed\n", 69966550Snyan device_get_nameunit(sc->sc_dev)); 70066550Snyan } 70166550Snyan timeout = 10000; 70266550Snyan while (((NIC_GET(sc, SNCR_ISR) & ISR_LCD) == 0) && timeout--) 70366550Snyan continue; 70466550Snyan 70566550Snyan if (NIC_GET(sc, SNCR_ISR) & ISR_LCD) 70666550Snyan NIC_PUT(sc, SNCR_ISR, ISR_LCD); 70766550Snyan else 70866550Snyan device_printf(sc->sc_dev, 70966550Snyan "CAM initialisation without interrupt\n"); 71066550Snyan} 71166550Snyan 71266550Snyan#ifdef SNCDEBUG 713179442Sjhbstatic void 714243455Snyancamdump(struct snc_softc *sc) 71566550Snyan{ 71666550Snyan int i; 71766550Snyan 71866550Snyan printf("CAM entries:\n"); 71966550Snyan NIC_PUT(sc, SNCR_CR, CR_RST); 72066550Snyan wbflush(); 72166550Snyan 72266550Snyan for (i = 0; i < 16; i++) { 723118607Sjhb u_short ap2, ap1, ap0; 72466550Snyan NIC_PUT(sc, SNCR_CEP, i); 72566550Snyan wbflush(); 72666550Snyan ap2 = NIC_GET(sc, SNCR_CAP2); 72766550Snyan ap1 = NIC_GET(sc, SNCR_CAP1); 72866550Snyan ap0 = NIC_GET(sc, SNCR_CAP0); 72966550Snyan printf("%d: ap2=0x%x ap1=0x%x ap0=0x%x\n", i, ap2, ap1, ap0); 73066550Snyan } 73166550Snyan printf("CAM enable 0x%x\n", NIC_GET(sc, SNCR_CEP)); 73266550Snyan 73366550Snyan NIC_PUT(sc, SNCR_CR, 0); 73466550Snyan wbflush(); 73566550Snyan} 73666550Snyan#endif 73766550Snyan 738179442Sjhbstatic void 739243455Snyaninitialise_tda(struct snc_softc *sc) 74066550Snyan{ 74166550Snyan struct mtd *mtd; 74266550Snyan int i; 74366550Snyan 74466550Snyan for (i = 0; i < NTDA; i++) { 74566550Snyan mtd = &sc->mtda[i]; 74666550Snyan mtd->mtd_mbuf = 0; 74766550Snyan } 74866550Snyan 74966550Snyan sc->mtd_hw = 0; 75066550Snyan sc->mtd_prev = NTDA - 1; 75166550Snyan sc->mtd_free = 0; 75266550Snyan sc->mtd_tlinko = TXP_FRAGOFF + 1*TXP_FRAGSIZE + TXP_FPTRLO; 75366550Snyan sc->mtd_pint = NTDA/2; 75466550Snyan 75566550Snyan NIC_PUT(sc, SNCR_UTDA, UPPER(sc->mtda[0].mtd_vtxp)); 75666550Snyan NIC_PUT(sc, SNCR_CTDA, LOWER(sc->mtda[0].mtd_vtxp)); 75766550Snyan} 75866550Snyan 759179442Sjhbstatic void 760243455Snyaninitialise_rda(struct snc_softc *sc) 76166550Snyan{ 76266550Snyan int i; 76366550Snyan u_int32_t vv_rda = 0; 76466550Snyan u_int32_t v_rda = 0; 76566550Snyan 76666550Snyan /* link the RDA's together into a circular list */ 76766550Snyan for (i = 0; i < (sc->sc_nrda - 1); i++) { 76866550Snyan v_rda = sc->v_rda + (i * RXPKT_SIZE(sc)); 76966550Snyan vv_rda = sc->v_rda + ((i+1) * RXPKT_SIZE(sc)); 77066550Snyan SWO(sc, v_rda, RXPKT_RLINK, LOWER(vv_rda)); 77166550Snyan SWO(sc, v_rda, RXPKT_INUSE, 1); 77266550Snyan } 77366550Snyan v_rda = sc->v_rda + ((sc->sc_nrda - 1) * RXPKT_SIZE(sc)); 77466550Snyan SWO(sc, v_rda, RXPKT_RLINK, LOWER(sc->v_rda) | EOL); 77566550Snyan SWO(sc, v_rda, RXPKT_INUSE, 1); 77666550Snyan 77766550Snyan /* mark end of receive descriptor list */ 77866550Snyan sc->sc_rdamark = sc->sc_nrda - 1; 77966550Snyan 78066550Snyan sc->sc_rxmark = 0; 78166550Snyan 78266550Snyan NIC_PUT(sc, SNCR_URDA, UPPER(sc->v_rda)); 78366550Snyan NIC_PUT(sc, SNCR_CRDA, LOWER(sc->v_rda)); 78466550Snyan wbflush(); 78566550Snyan} 78666550Snyan 787179442Sjhbstatic void 788243455Snyaninitialise_rra(struct snc_softc *sc) 78966550Snyan{ 79066550Snyan int i; 79166550Snyan u_int v; 79266550Snyan int bitmode = sc->bitmode; 79366550Snyan 79466550Snyan if (bitmode) 79566550Snyan NIC_PUT(sc, SNCR_EOBC, RBASIZE(sc) / 2 - 2); 79666550Snyan else 79766550Snyan NIC_PUT(sc, SNCR_EOBC, RBASIZE(sc) / 2 - 1); 79866550Snyan 79966550Snyan NIC_PUT(sc, SNCR_URRA, UPPER(sc->v_rra[0])); 80066550Snyan NIC_PUT(sc, SNCR_RSA, LOWER(sc->v_rra[0])); 80166550Snyan /* rea must point just past the end of the rra space */ 80266550Snyan NIC_PUT(sc, SNCR_REA, LOWER(sc->v_rea)); 80366550Snyan NIC_PUT(sc, SNCR_RRP, LOWER(sc->v_rra[0])); 80466550Snyan NIC_PUT(sc, SNCR_RSC, 0); 80566550Snyan 80666550Snyan /* fill up SOME of the rra with buffers */ 80766550Snyan for (i = 0; i < NRBA; i++) { 80866550Snyan v = SONIC_GETDMA(sc->rbuf[i]); 80966550Snyan SWO(sc, sc->v_rra[i], RXRSRC_PTRHI, UPPER(v)); 81066550Snyan SWO(sc, sc->v_rra[i], RXRSRC_PTRLO, LOWER(v)); 811179442Sjhb SWO(sc, sc->v_rra[i], RXRSRC_WCHI, UPPER(PAGE_SIZE/2)); 812179442Sjhb SWO(sc, sc->v_rra[i], RXRSRC_WCLO, LOWER(PAGE_SIZE/2)); 81366550Snyan } 81466550Snyan sc->sc_rramark = NRBA; 81566550Snyan NIC_PUT(sc, SNCR_RWP, LOWER(sc->v_rra[sc->sc_rramark])); 81666550Snyan wbflush(); 81766550Snyan} 81866550Snyan 81966550Snyanvoid 820243455Snyansncintr(void *arg) 82166550Snyan{ 82266550Snyan struct snc_softc *sc = (struct snc_softc *)arg; 82366550Snyan int isr; 82466550Snyan 82566550Snyan if (sc->sc_enabled == 0) 82666550Snyan return; 82766550Snyan 828181298Sjhb SNC_LOCK(sc); 82966550Snyan while ((isr = (NIC_GET(sc, SNCR_ISR) & ISR_ALL)) != 0) { 83066550Snyan /* scrub the interrupts that we are going to service */ 83166550Snyan NIC_PUT(sc, SNCR_ISR, isr); 83266550Snyan wbflush(); 83366550Snyan 83466550Snyan if (isr & (ISR_BR | ISR_LCD | ISR_TC)) 83566550Snyan device_printf(sc->sc_dev, 83666550Snyan "unexpected interrupt status 0x%x\n", 83766550Snyan isr); 83866550Snyan 83966550Snyan if (isr & (ISR_TXDN | ISR_TXER | ISR_PINT)) 84066550Snyan sonictxint(sc); 84166550Snyan 84266550Snyan if (isr & ISR_PKTRX) 84366550Snyan sonicrxint(sc); 84466550Snyan 84566550Snyan if (isr & (ISR_HBL | ISR_RDE | ISR_RBE | ISR_RBAE | ISR_RFO)) { 84666550Snyan if (isr & ISR_HBL) 84766550Snyan /* 84866550Snyan * The repeater is not providing a heartbeat. 84966550Snyan * In itself this isn't harmful, lots of the 85066550Snyan * cheap repeater hubs don't supply a heartbeat. 85166550Snyan * So ignore the lack of heartbeat. Its only 85266550Snyan * if we can't detect a carrier that we have a 85366550Snyan * problem. 85466550Snyan */ 85566550Snyan ; 85666550Snyan if (isr & ISR_RDE) 85766550Snyan device_printf(sc->sc_dev, 85866550Snyan "receive descriptors exhausted\n"); 85966550Snyan if (isr & ISR_RBE) 86066550Snyan device_printf(sc->sc_dev, 86166550Snyan "receive buffers exhausted\n"); 86266550Snyan if (isr & ISR_RBAE) 86366550Snyan device_printf(sc->sc_dev, 86466550Snyan "receive buffer area exhausted\n"); 86566550Snyan if (isr & ISR_RFO) 86666550Snyan device_printf(sc->sc_dev, 86766550Snyan "receive FIFO overrun\n"); 86866550Snyan } 86966550Snyan if (isr & (ISR_CRC | ISR_FAE | ISR_MP)) { 87066550Snyan#ifdef notdef 87166550Snyan if (isr & ISR_CRC) 87266550Snyan sc->sc_crctally++; 87366550Snyan if (isr & ISR_FAE) 87466550Snyan sc->sc_faetally++; 87566550Snyan if (isr & ISR_MP) 87666550Snyan sc->sc_mptally++; 87766550Snyan#endif 87866550Snyan } 879181298Sjhb sncstart_locked(sc->sc_ifp); 88066550Snyan } 881181298Sjhb SNC_UNLOCK(sc); 88266550Snyan return; 88366550Snyan} 88466550Snyan 88566550Snyan/* 88666550Snyan * Transmit interrupt routine 88766550Snyan */ 888179442Sjhbstatic void 889243455Snyansonictxint(struct snc_softc *sc) 89066550Snyan{ 89166550Snyan struct mtd *mtd; 89266550Snyan u_int32_t txp; 89366550Snyan unsigned short txp_status; 89466550Snyan int mtd_hw; 895147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 89666550Snyan 89766550Snyan mtd_hw = sc->mtd_hw; 89866550Snyan 89966550Snyan if (mtd_hw == sc->mtd_free) 90066550Snyan return; 90166550Snyan 90266550Snyan while (mtd_hw != sc->mtd_free) { 90366550Snyan mtd = &sc->mtda[mtd_hw]; 90466550Snyan 90566550Snyan txp = mtd->mtd_vtxp; 90666550Snyan 90766550Snyan if (SRO(sc, txp, TXP_STATUS) == 0) { 90866550Snyan break; /* it hasn't really gone yet */ 90966550Snyan } 91066550Snyan 91166550Snyan#ifdef SNCDEBUG 91266550Snyan if ((sncdebug & SNC_SHOWTXHDR) != 0) 91366550Snyan { 91466550Snyan struct ether_header eh; 91566550Snyan 91666550Snyan (*sc->sc_copyfrombuf)(sc, &eh, mtd->mtd_vbuf, sizeof(eh)); 91766550Snyan device_printf(sc->sc_dev, 91866550Snyan "xmit status=0x%x len=%d type=0x%x from %6D", 91966550Snyan SRO(sc, txp, TXP_STATUS), 92066550Snyan SRO(sc, txp, TXP_PKTSIZE), 92166550Snyan htons(eh.ether_type), 92266550Snyan eh.ether_shost, ":"); 92366550Snyan printf(" (to %6D)\n", eh.ether_dhost, ":"); 92466550Snyan } 92566550Snyan#endif /* SNCDEBUG */ 92666550Snyan 927181298Sjhb sc->sc_tx_timeout = 0; 928148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 92966550Snyan 93066550Snyan if (mtd->mtd_mbuf != 0) { 93166550Snyan m_freem(mtd->mtd_mbuf); 93266550Snyan mtd->mtd_mbuf = 0; 93366550Snyan } 93466550Snyan if (++mtd_hw == NTDA) mtd_hw = 0; 93566550Snyan 93666550Snyan txp_status = SRO(sc, txp, TXP_STATUS); 93766550Snyan 93866550Snyan ifp->if_collisions += (txp_status & TCR_EXC) ? 16 : 93966550Snyan ((txp_status & TCR_NC) >> 12); 94066550Snyan 94166550Snyan if ((txp_status & TCR_PTX) == 0) { 94266550Snyan ifp->if_oerrors++; 94366550Snyan device_printf(sc->sc_dev, "Tx packet status=0x%x\n", 94466550Snyan txp_status); 94566550Snyan 94666550Snyan /* XXX - DG This looks bogus */ 94766550Snyan if (mtd_hw != sc->mtd_free) { 94866550Snyan printf("resubmitting remaining packets\n"); 94966550Snyan mtd = &sc->mtda[mtd_hw]; 95066550Snyan NIC_PUT(sc, SNCR_CTDA, LOWER(mtd->mtd_vtxp)); 95166550Snyan NIC_PUT(sc, SNCR_CR, CR_TXP); 95266550Snyan wbflush(); 95366550Snyan break; 95466550Snyan } 95566550Snyan } 95666550Snyan } 95766550Snyan 95866550Snyan sc->mtd_hw = mtd_hw; 95966550Snyan return; 96066550Snyan} 96166550Snyan 96266550Snyan/* 96366550Snyan * Receive interrupt routine 96466550Snyan */ 965179442Sjhbstatic void 966243455Snyansonicrxint(struct snc_softc *sc) 96766550Snyan{ 96866550Snyan u_int32_t rda; 96966550Snyan int orra; 97066550Snyan int len; 97166550Snyan int rramark; 97266550Snyan int rdamark; 97366550Snyan u_int16_t rxpkt_ptr; 97466550Snyan 97566550Snyan rda = sc->v_rda + (sc->sc_rxmark * RXPKT_SIZE(sc)); 97666550Snyan 97766550Snyan while (SRO(sc, rda, RXPKT_INUSE) == 0) { 97866550Snyan u_int status = SRO(sc, rda, RXPKT_STATUS); 97966550Snyan 98066550Snyan orra = RBASEQ(SRO(sc, rda, RXPKT_SEQNO)) & RRAMASK; 98166550Snyan rxpkt_ptr = SRO(sc, rda, RXPKT_PTRLO); 98266550Snyan /* 98366550Snyan * Do not trunc ether_header length. 98466550Snyan * Our sonic_read() and sonic_get() require it. 98566550Snyan */ 98666550Snyan len = SRO(sc, rda, RXPKT_BYTEC) - FCSSIZE; 98766550Snyan if (status & RCR_PRX) { 988179442Sjhb /* XXX: Does PAGE_MASK require? */ 98966550Snyan u_int32_t pkt = 990179442Sjhb sc->rbuf[orra & RBAMASK] + (rxpkt_ptr & PAGE_MASK); 99166550Snyan if (sonic_read(sc, pkt, len)) 992147256Sbrooks sc->sc_ifp->if_ipackets++; 99366550Snyan else 994147256Sbrooks sc->sc_ifp->if_ierrors++; 99566550Snyan } else 996147256Sbrooks sc->sc_ifp->if_ierrors++; 99766550Snyan 99866550Snyan /* 99966550Snyan * give receive buffer area back to chip. 100066550Snyan * 100166550Snyan * If this was the last packet in the RRA, give the RRA to 100266550Snyan * the chip again. 100366550Snyan * If sonic read didnt copy it out then we would have to 100466550Snyan * wait !! 100566550Snyan * (dont bother add it back in again straight away) 100666550Snyan * 100766550Snyan * Really, we're doing v_rra[rramark] = v_rra[orra] but 100866550Snyan * we have to use the macros because SONIC might be in 100966550Snyan * 16 or 32 bit mode. 101066550Snyan */ 101166550Snyan if (status & RCR_LPKT) { 101266550Snyan u_int32_t tmp1, tmp2; 101366550Snyan 101466550Snyan rramark = sc->sc_rramark; 101566550Snyan tmp1 = sc->v_rra[rramark]; 101666550Snyan tmp2 = sc->v_rra[orra]; 101766550Snyan SWO(sc, tmp1, RXRSRC_PTRLO, 101866550Snyan SRO(sc, tmp2, RXRSRC_PTRLO)); 101966550Snyan SWO(sc, tmp1, RXRSRC_PTRHI, 102066550Snyan SRO(sc, tmp2, RXRSRC_PTRHI)); 102166550Snyan SWO(sc, tmp1, RXRSRC_WCLO, 102266550Snyan SRO(sc, tmp2, RXRSRC_WCLO)); 102366550Snyan SWO(sc, tmp1, RXRSRC_WCHI, 102466550Snyan SRO(sc, tmp2, RXRSRC_WCHI)); 102566550Snyan 102666550Snyan /* zap old rra for fun */ 102766550Snyan SWO(sc, tmp2, RXRSRC_WCHI, 0); 102866550Snyan SWO(sc, tmp2, RXRSRC_WCLO, 0); 102966550Snyan 103066550Snyan sc->sc_rramark = (++rramark) & RRAMASK; 103166550Snyan NIC_PUT(sc, SNCR_RWP, LOWER(sc->v_rra[rramark])); 103266550Snyan wbflush(); 103366550Snyan } 103466550Snyan 103566550Snyan /* 103666550Snyan * give receive descriptor back to chip simple 103766550Snyan * list is circular 103866550Snyan */ 103966550Snyan rdamark = sc->sc_rdamark; 104066550Snyan SWO(sc, rda, RXPKT_INUSE, 1); 104166550Snyan SWO(sc, rda, RXPKT_RLINK, 104266550Snyan SRO(sc, rda, RXPKT_RLINK) | EOL); 104366550Snyan SWO(sc, (sc->v_rda + (rdamark * RXPKT_SIZE(sc))), RXPKT_RLINK, 104466550Snyan SRO(sc, (sc->v_rda + (rdamark * RXPKT_SIZE(sc))), 104566550Snyan RXPKT_RLINK) & ~EOL); 104666550Snyan sc->sc_rdamark = sc->sc_rxmark; 104766550Snyan 104866550Snyan if (++sc->sc_rxmark >= sc->sc_nrda) 104966550Snyan sc->sc_rxmark = 0; 105066550Snyan rda = sc->v_rda + (sc->sc_rxmark * RXPKT_SIZE(sc)); 105166550Snyan } 105266550Snyan} 105366550Snyan 105466550Snyan/* 105566550Snyan * sonic_read -- pull packet off interface and forward to 105666550Snyan * appropriate protocol handler 105766550Snyan */ 1058179442Sjhbstatic int 1059243455Snyansonic_read(struct snc_softc *sc, u_int32_t pkt, int len) 106066550Snyan{ 1061147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 106266550Snyan struct ether_header *et; 106366550Snyan struct mbuf *m; 106466550Snyan 106566550Snyan if (len <= sizeof(struct ether_header) || 106666550Snyan len > ETHERMTU + sizeof(struct ether_header)) { 106766550Snyan device_printf(sc->sc_dev, 106866550Snyan "invalid packet length %d bytes\n", len); 106966550Snyan return (0); 107066550Snyan } 107166550Snyan 107266550Snyan /* Pull packet off interface. */ 107366550Snyan m = sonic_get(sc, pkt, len); 107466550Snyan if (m == 0) { 107566550Snyan return (0); 107666550Snyan } 107766550Snyan 107866550Snyan /* We assume that the header fit entirely in one mbuf. */ 107966550Snyan et = mtod(m, struct ether_header *); 108066550Snyan 108166550Snyan#ifdef SNCDEBUG 108266550Snyan if ((sncdebug & SNC_SHOWRXHDR) != 0) 108366550Snyan { 108466550Snyan device_printf(sc->sc_dev, "rcvd 0x%x len=%d type=0x%x from %6D", 108566550Snyan pkt, len, htons(et->ether_type), 108666550Snyan et->ether_shost, ":"); 108766550Snyan printf(" (to %6D)\n", et->ether_dhost, ":"); 108866550Snyan } 108966550Snyan#endif /* SNCDEBUG */ 109066550Snyan 1091106937Ssam /* Pass the packet up. */ 1092181298Sjhb SNC_UNLOCK(sc); 1093106937Ssam (*ifp->if_input)(ifp, m); 1094181298Sjhb SNC_LOCK(sc); 109566550Snyan return (1); 109666550Snyan} 109766550Snyan 109866550Snyan 109966550Snyan/* 110066550Snyan * munge the received packet into an mbuf chain 110166550Snyan */ 1102179442Sjhbstatic struct mbuf * 1103243455Snyansonic_get(struct snc_softc *sc, u_int32_t pkt, int datalen) 110466550Snyan{ 110566550Snyan struct mbuf *m, *top, **mp; 110666550Snyan int len; 110766550Snyan /* 110866550Snyan * Do not trunc ether_header length. 110966550Snyan * Our sonic_read() and sonic_get() require it. 111066550Snyan */ 111166550Snyan 1112248078Smarius MGETHDR(m, M_NOWAIT, MT_DATA); 111366550Snyan if (m == 0) 111466550Snyan return (0); 1115147256Sbrooks m->m_pkthdr.rcvif = sc->sc_ifp; 111666550Snyan m->m_pkthdr.len = datalen; 111766550Snyan len = MHLEN; 111866550Snyan top = 0; 111966550Snyan mp = ⊤ 112066550Snyan 112166550Snyan while (datalen > 0) { 112266550Snyan if (top) { 1123248078Smarius MGET(m, M_NOWAIT, MT_DATA); 112466550Snyan if (m == 0) { 112566550Snyan m_freem(top); 112666550Snyan return (0); 112766550Snyan } 112866550Snyan len = MLEN; 112966550Snyan } 113066550Snyan if (datalen >= MINCLSIZE) { 1131248078Smarius MCLGET(m, M_NOWAIT); 113266550Snyan if ((m->m_flags & M_EXT) == 0) { 113366550Snyan if (top) m_freem(top); 113466550Snyan return (0); 113566550Snyan } 113666550Snyan len = MCLBYTES; 113766550Snyan } 113866550Snyan#if 0 113966550Snyan /* XXX: Require? */ 114066550Snyan if (!top) { 114166550Snyan register int pad = 114266550Snyan ALIGN(sizeof(struct ether_header)) - 114366550Snyan sizeof(struct ether_header); 114466550Snyan m->m_data += pad; 114566550Snyan len -= pad; 114666550Snyan } 114766550Snyan#endif 114866550Snyan m->m_len = len = min(datalen, len); 114966550Snyan 115066550Snyan (*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), pkt, len); 115166550Snyan pkt += len; 115266550Snyan datalen -= len; 115366550Snyan *mp = m; 115466550Snyan mp = &m->m_next; 115566550Snyan } 115666550Snyan 115766550Snyan return (top); 115866550Snyan} 115966550Snyan/* 116066550Snyan * Enable power on the interface. 116166550Snyan */ 116266550Snyanint 1163243455Snyansnc_enable(struct snc_softc *sc) 116466550Snyan{ 116566550Snyan 116666550Snyan#ifdef SNCDEBUG 116766550Snyan device_printf(sc->sc_dev, "snc_enable()\n"); 116866550Snyan#endif /* SNCDEBUG */ 116966550Snyan 117066550Snyan if (sc->sc_enabled == 0 && sc->sc_enable != NULL) { 117166550Snyan if ((*sc->sc_enable)(sc) != 0) { 117266550Snyan device_printf(sc->sc_dev, "device enable failed\n"); 117366550Snyan return (EIO); 117466550Snyan } 117566550Snyan } 117666550Snyan 117766550Snyan sc->sc_enabled = 1; 117866550Snyan return (0); 117966550Snyan} 118066550Snyan 118166550Snyan/* 118266550Snyan * Disable power on the interface. 118366550Snyan */ 118466550Snyanvoid 1185243455Snyansnc_disable(struct snc_softc *sc) 118666550Snyan{ 118766550Snyan 118866550Snyan#ifdef SNCDEBUG 118966550Snyan device_printf(sc->sc_dev, "snc_disable()\n"); 119066550Snyan#endif /* SNCDEBUG */ 119166550Snyan 119266550Snyan if (sc->sc_enabled != 0 && sc->sc_disable != NULL) { 119366550Snyan (*sc->sc_disable)(sc); 119466550Snyan sc->sc_enabled = 0; 119566550Snyan } 119666550Snyan} 119766550Snyan 119866550Snyan 1199