166550Snyan/* $FreeBSD: releng/10.3/sys/dev/snc/dp83932.c 243857 2012-12-04 09:32:43Z glebius $ */ 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 145242871Snyansncconfig(struct snc_softc *sc, int *media, int nmedia, int defmedia, 146242871Snyan 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; 177207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 17866550Snyan 17966550Snyan /* Initialize media goo. */ 18066550Snyan ifmedia_init(&sc->sc_media, 0, snc_mediachange, 18166550Snyan snc_mediastatus); 18266550Snyan if (media != NULL) { 18366550Snyan for (i = 0; i < nmedia; i++) 18466550Snyan ifmedia_add(&sc->sc_media, media[i], 0, NULL); 18566550Snyan ifmedia_set(&sc->sc_media, defmedia); 18666550Snyan } else { 18766550Snyan ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL); 18866550Snyan ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL); 18966550Snyan } 19066550Snyan 191106937Ssam ether_ifattach(ifp, myea); 192181298Sjhb return (0); 19366550Snyan} 19466550Snyan 19566550Snyanvoid 196242871Snyansncshutdown(void *arg) 19766550Snyan{ 198181298Sjhb struct snc_softc *sc = arg; 19966550Snyan 200181298Sjhb SNC_ASSERT_LOCKED(sc); 201181298Sjhb sncstop(sc); 20266550Snyan} 20366550Snyan 20466550Snyan/* 20566550Snyan * Media change callback. 20666550Snyan */ 20766550Snyanint 208242871Snyansnc_mediachange(struct ifnet *ifp) 20966550Snyan{ 21066550Snyan struct snc_softc *sc = ifp->if_softc; 211181298Sjhb int error; 21266550Snyan 213181298Sjhb SNC_LOCK(sc); 21466550Snyan if (sc->sc_mediachange) 215181298Sjhb error = (*sc->sc_mediachange)(sc); 216181298Sjhb else 217181298Sjhb error = EINVAL; 218181298Sjhb SNC_UNLOCK(sc); 219181298Sjhb return (error); 22066550Snyan} 22166550Snyan 22266550Snyan/* 22366550Snyan * Media status callback. 22466550Snyan */ 22566550Snyanvoid 226242871Snyansnc_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) 22766550Snyan{ 22866550Snyan struct snc_softc *sc = ifp->if_softc; 22966550Snyan 230181298Sjhb SNC_LOCK(sc); 23166550Snyan if (sc->sc_enabled == 0) { 23266550Snyan ifmr->ifm_active = IFM_ETHER | IFM_NONE; 23366550Snyan ifmr->ifm_status = 0; 234181298Sjhb SNC_UNLOCK(sc); 23566550Snyan return; 23666550Snyan } 23766550Snyan 23866550Snyan if (sc->sc_mediastatus) 23966550Snyan (*sc->sc_mediastatus)(sc, ifmr); 240181298Sjhb SNC_UNLOCK(sc); 24166550Snyan} 24266550Snyan 24366550Snyan 244179442Sjhbstatic int 245242871Snyansncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 24666550Snyan{ 24766550Snyan struct ifreq *ifr; 24866550Snyan struct snc_softc *sc = ifp->if_softc; 249181298Sjhb int err = 0; 25066550Snyan 25166550Snyan switch (cmd) { 25266550Snyan 25366550Snyan case SIOCSIFFLAGS: 254181298Sjhb SNC_LOCK(sc); 25566550Snyan if ((ifp->if_flags & IFF_UP) == 0 && 256148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 25766550Snyan /* 25866550Snyan * If interface is marked down and it is running, 25966550Snyan * then stop it. 26066550Snyan */ 26166550Snyan sncstop(sc); 26266550Snyan snc_disable(sc); 26366550Snyan } else if ((ifp->if_flags & IFF_UP) != 0 && 264148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 26566550Snyan /* 26666550Snyan * If interface is marked up and it is stopped, 26766550Snyan * then start it. 26866550Snyan */ 26966550Snyan if ((err = snc_enable(sc)) != 0) 27066550Snyan break; 271181298Sjhb sncinit_locked(sc); 27266550Snyan } else if (sc->sc_enabled) { 27366550Snyan /* 27466550Snyan * reset the interface to pick up any other changes 27566550Snyan * in flags 27666550Snyan */ 27766550Snyan sncreset(sc); 278181298Sjhb sncstart_locked(ifp); 27966550Snyan } 280181298Sjhb SNC_UNLOCK(sc); 28166550Snyan break; 28266550Snyan 28366550Snyan case SIOCADDMULTI: 28466550Snyan case SIOCDELMULTI: 285181298Sjhb SNC_LOCK(sc); 28666550Snyan if (sc->sc_enabled == 0) { 28766550Snyan err = EIO; 288181298Sjhb SNC_UNLOCK(sc); 28966550Snyan break; 29066550Snyan } 29166550Snyan sncreset(sc); 292181298Sjhb SNC_UNLOCK(sc); 29366550Snyan err = 0; 29466550Snyan break; 29566550Snyan case SIOCGIFMEDIA: 29666550Snyan case SIOCSIFMEDIA: 29766550Snyan ifr = (struct ifreq *) data; 29866550Snyan err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 29966550Snyan break; 30066550Snyan default: 301106937Ssam err = ether_ioctl(ifp, cmd, data); 302106937Ssam break; 30366550Snyan } 30466550Snyan return (err); 30566550Snyan} 30666550Snyan 30766550Snyan/* 30866550Snyan * Encapsulate a packet of type family for the local net. 30966550Snyan */ 310179442Sjhbstatic void 311242871Snyansncstart(struct ifnet *ifp) 31266550Snyan{ 31366550Snyan struct snc_softc *sc = ifp->if_softc; 314181298Sjhb 315181298Sjhb SNC_LOCK(sc); 316181298Sjhb sncstart_locked(ifp); 317181298Sjhb SNC_UNLOCK(sc); 318181298Sjhb} 319181298Sjhb 320181298Sjhbstatic void 321242871Snyansncstart_locked(struct ifnet *ifp) 322181298Sjhb{ 323181298Sjhb struct snc_softc *sc = ifp->if_softc; 32466550Snyan struct mbuf *m; 32566550Snyan int mtd_next; 32666550Snyan 327148887Srwatson if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 328148887Srwatson IFF_DRV_RUNNING) 32966550Snyan return; 33066550Snyan 33166550Snyanoutloop: 33266550Snyan /* Check for room in the xmit buffer. */ 33366550Snyan if ((mtd_next = (sc->mtd_free + 1)) == NTDA) 33466550Snyan mtd_next = 0; 33566550Snyan 33666550Snyan if (mtd_next == sc->mtd_hw) { 337148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 33866550Snyan return; 33966550Snyan } 34066550Snyan 34166550Snyan IF_DEQUEUE(&ifp->if_snd, m); 34266550Snyan if (m == 0) 34366550Snyan return; 34466550Snyan 34566550Snyan /* We need the header for m_pkthdr.len. */ 346113255Sdes M_ASSERTPKTHDR(m); 34766550Snyan 34866550Snyan /* 34966550Snyan * If there is nothing in the o/p queue, and there is room in 35066550Snyan * the Tx ring, then send the packet directly. Otherwise append 35166550Snyan * it to the o/p queue. 35266550Snyan */ 35366550Snyan if ((sonicput(sc, m, mtd_next)) == 0) { 35466550Snyan IF_PREPEND(&ifp->if_snd, m); 35566550Snyan return; 35666550Snyan } 35766550Snyan 358142180Smlaier /* 359142180Smlaier * If bpf is listening on this interface, let it see the packet 360142180Smlaier * before we commit it to the wire, but only if we are really 361142180Smlaier * committed to send it. 362142180Smlaier * 363142180Smlaier * XXX: Locking must protect m against premature m_freem() in 364142180Smlaier * sonictxint(). 365142180Smlaier */ 366142180Smlaier BPF_MTAP(ifp, m); 367142180Smlaier 36866550Snyan sc->mtd_prev = sc->mtd_free; 36966550Snyan sc->mtd_free = mtd_next; 37066550Snyan 37166550Snyan ifp->if_opackets++; /* # of pkts */ 37266550Snyan 37366550Snyan /* Jump back for possibly more punishment. */ 37466550Snyan goto outloop; 37566550Snyan} 37666550Snyan 37766550Snyan/* 37866550Snyan * reset and restart the SONIC. Called in case of fatal 37966550Snyan * hardware/software errors. 38066550Snyan */ 381179442Sjhbstatic void 382242871Snyansncreset(struct snc_softc *sc) 38366550Snyan{ 38466550Snyan sncstop(sc); 385181298Sjhb sncinit_locked(sc); 38666550Snyan} 38766550Snyan 388179442Sjhbstatic void 389242871Snyansncinit(void *xsc) 39066550Snyan{ 39166550Snyan struct snc_softc *sc = xsc; 392181298Sjhb 393181298Sjhb SNC_LOCK(sc); 394181298Sjhb sncinit_locked(sc); 395181298Sjhb SNC_UNLOCK(sc); 396181298Sjhb} 397181298Sjhb 398181298Sjhbstatic void 399181298Sjhbsncinit_locked(struct snc_softc *sc) 400181298Sjhb{ 40166550Snyan u_long s_rcr; 40266550Snyan 403148887Srwatson if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 40466550Snyan /* already running */ 40566550Snyan return; 40666550Snyan 40766550Snyan NIC_PUT(sc, SNCR_CR, CR_RST); /* DCR only accessable in reset mode! */ 40866550Snyan 40966550Snyan /* config it */ 41066550Snyan NIC_PUT(sc, SNCR_DCR, (sc->sncr_dcr | 41166550Snyan (sc->bitmode ? DCR_DW32 : DCR_DW16))); 41266550Snyan NIC_PUT(sc, SNCR_DCR2, sc->sncr_dcr2); 41366550Snyan 41466550Snyan s_rcr = RCR_BRD | RCR_LBNONE; 415147256Sbrooks if (sc->sc_ifp->if_flags & IFF_PROMISC) 41666550Snyan s_rcr |= RCR_PRO; 417147256Sbrooks if (sc->sc_ifp->if_flags & IFF_ALLMULTI) 41866550Snyan s_rcr |= RCR_AMC; 41966550Snyan NIC_PUT(sc, SNCR_RCR, s_rcr); 42066550Snyan 42166550Snyan NIC_PUT(sc, SNCR_IMR, (IMR_PRXEN | IMR_PTXEN | IMR_TXEREN | IMR_LCDEN)); 42266550Snyan 42366550Snyan /* clear pending interrupts */ 42466550Snyan NIC_PUT(sc, SNCR_ISR, ISR_ALL); 42566550Snyan 42666550Snyan /* clear tally counters */ 42766550Snyan NIC_PUT(sc, SNCR_CRCT, -1); 42866550Snyan NIC_PUT(sc, SNCR_FAET, -1); 42966550Snyan NIC_PUT(sc, SNCR_MPT, -1); 43066550Snyan 43166550Snyan initialise_tda(sc); 43266550Snyan initialise_rda(sc); 43366550Snyan initialise_rra(sc); 43466550Snyan 43566550Snyan /* enable the chip */ 43666550Snyan NIC_PUT(sc, SNCR_CR, 0); 43766550Snyan wbflush(); 43866550Snyan 43966550Snyan /* program the CAM */ 44066550Snyan camprogram(sc); 44166550Snyan 44266550Snyan /* get it to read resource descriptors */ 44366550Snyan NIC_PUT(sc, SNCR_CR, CR_RRRA); 44466550Snyan wbflush(); 44566550Snyan while ((NIC_GET(sc, SNCR_CR)) & CR_RRRA) 44666550Snyan continue; 44766550Snyan 44866550Snyan /* enable rx */ 44966550Snyan NIC_PUT(sc, SNCR_CR, CR_RXEN); 45066550Snyan wbflush(); 45166550Snyan 45266550Snyan /* flag interface as "running" */ 453148887Srwatson sc->sc_ifp->if_drv_flags |= IFF_DRV_RUNNING; 454148887Srwatson sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 455181298Sjhb callout_reset(&sc->sc_timer, hz, sncwatchdog, sc); 45666550Snyan 45766550Snyan return; 45866550Snyan} 45966550Snyan 46066550Snyan/* 46166550Snyan * close down an interface and free its buffers 46266550Snyan * Called on final close of device, or if sncinit() fails 46366550Snyan * part way through. 46466550Snyan */ 465179442Sjhbstatic int 466242871Snyansncstop(struct snc_softc *sc) 46766550Snyan{ 46866550Snyan struct mtd *mtd; 46966550Snyan 470181298Sjhb SNC_ASSERT_LOCKED(sc); 471181298Sjhb 47266550Snyan /* stick chip in reset */ 47366550Snyan NIC_PUT(sc, SNCR_CR, CR_RST); 47466550Snyan wbflush(); 47566550Snyan 47666550Snyan /* free all receive buffers (currently static so nothing to do) */ 47766550Snyan 47866550Snyan /* free all pending transmit mbufs */ 47966550Snyan while (sc->mtd_hw != sc->mtd_free) { 48066550Snyan mtd = &sc->mtda[sc->mtd_hw]; 48166550Snyan if (mtd->mtd_mbuf) 48266550Snyan m_freem(mtd->mtd_mbuf); 48366550Snyan if (++sc->mtd_hw == NTDA) sc->mtd_hw = 0; 48466550Snyan } 48566550Snyan 486181298Sjhb callout_stop(&sc->sc_timer); 487181298Sjhb sc->sc_tx_timeout = 0; 488181298Sjhb sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 48966550Snyan 49066550Snyan return (0); 49166550Snyan} 49266550Snyan 49366550Snyan/* 49466550Snyan * Called if any Tx packets remain unsent after 5 seconds, 49566550Snyan * In all cases we just reset the chip, and any retransmission 49666550Snyan * will be handled by higher level protocol timeouts. 49766550Snyan */ 498179442Sjhbstatic void 499181298Sjhbsncwatchdog(void *arg) 50066550Snyan{ 501181298Sjhb struct snc_softc *sc = arg; 50266550Snyan struct mtd *mtd; 50366550Snyan 504181298Sjhb SNC_ASSERT_LOCKED(sc); 505181298Sjhb if (sc->sc_tx_timeout && --sc->sc_tx_timeout == 0) { 506181298Sjhb if (sc->mtd_hw != sc->mtd_free) { 507181298Sjhb /* something still pending for transmit */ 508181298Sjhb mtd = &sc->mtda[sc->mtd_hw]; 509181298Sjhb if (SRO(sc, mtd->mtd_vtxp, TXP_STATUS) == 0) 510181298Sjhb log(LOG_ERR, "%s: Tx - timeout\n", 511181298Sjhb device_get_nameunit(sc->sc_dev)); 512181298Sjhb else 513181298Sjhb log(LOG_ERR, "%s: Tx - lost interrupt\n", 514181298Sjhb device_get_nameunit(sc->sc_dev)); 515181298Sjhb sncreset(sc); 516181298Sjhb } 51766550Snyan } 518181298Sjhb callout_reset(&sc->sc_timer, hz, sncwatchdog, sc); 51966550Snyan} 52066550Snyan 52166550Snyan/* 522181298Sjhb * stuff packet into sonic 52366550Snyan */ 524179442Sjhbstatic u_int 525242871Snyansonicput(struct snc_softc *sc, struct mbuf *m0, int mtd_next) 52666550Snyan{ 52766550Snyan struct mtd *mtdp; 52866550Snyan struct mbuf *m; 52966550Snyan u_int32_t buff; 53066550Snyan u_int32_t txp; 53166550Snyan u_int len = 0; 53266550Snyan u_int totlen = 0; 53366550Snyan 53466550Snyan#ifdef whyonearthwouldyoudothis 53566550Snyan if (NIC_GET(sc, SNCR_CR) & CR_TXP) 53666550Snyan return (0); 53766550Snyan#endif 53866550Snyan 53966550Snyan /* grab the replacement mtd */ 54066550Snyan mtdp = &sc->mtda[sc->mtd_free]; 54166550Snyan 54266550Snyan buff = mtdp->mtd_vbuf; 54366550Snyan 54466550Snyan /* this packet goes to mtdnext fill in the TDA */ 54566550Snyan mtdp->mtd_mbuf = m0; 54666550Snyan txp = mtdp->mtd_vtxp; 54766550Snyan 54866550Snyan /* Write to the config word. Every (NTDA/2)+1 packets we set an intr */ 54966550Snyan if (sc->mtd_pint == 0) { 55066550Snyan sc->mtd_pint = NTDA/2; 55166550Snyan SWO(sc, txp, TXP_CONFIG, TCR_PINT); 55266550Snyan } else { 55366550Snyan sc->mtd_pint--; 55466550Snyan SWO(sc, txp, TXP_CONFIG, 0); 55566550Snyan } 55666550Snyan 55766550Snyan for (m = m0; m; m = m->m_next) { 55866550Snyan len = m->m_len; 55966550Snyan totlen += len; 56066550Snyan (*sc->sc_copytobuf)(sc, mtod(m, caddr_t), buff, len); 56166550Snyan buff += len; 56266550Snyan } 56366550Snyan if (totlen >= TXBSIZE) { 56466550Snyan panic("%s: sonicput: packet overflow", 56566550Snyan device_get_nameunit(sc->sc_dev)); 56666550Snyan } 56766550Snyan 56866550Snyan SWO(sc, txp, TXP_FRAGOFF + (0 * TXP_FRAGSIZE) + TXP_FPTRLO, 56966550Snyan LOWER(mtdp->mtd_vbuf)); 57066550Snyan SWO(sc, txp, TXP_FRAGOFF + (0 * TXP_FRAGSIZE) + TXP_FPTRHI, 57166550Snyan UPPER(mtdp->mtd_vbuf)); 57266550Snyan 57366550Snyan if (totlen < ETHERMIN + sizeof(struct ether_header)) { 57466550Snyan int pad = ETHERMIN + sizeof(struct ether_header) - totlen; 57566550Snyan (*sc->sc_zerobuf)(sc, mtdp->mtd_vbuf + totlen, pad); 57666550Snyan totlen = ETHERMIN + sizeof(struct ether_header); 57766550Snyan } 57866550Snyan 57966550Snyan SWO(sc, txp, TXP_FRAGOFF + (0 * TXP_FRAGSIZE) + TXP_FSIZE, 58066550Snyan totlen); 58166550Snyan SWO(sc, txp, TXP_FRAGCNT, 1); 58266550Snyan SWO(sc, txp, TXP_PKTSIZE, totlen); 58366550Snyan 58466550Snyan /* link onto the next mtd that will be used */ 58566550Snyan SWO(sc, txp, TXP_FRAGOFF + (1 * TXP_FRAGSIZE) + TXP_FPTRLO, 58666550Snyan LOWER(sc->mtda[mtd_next].mtd_vtxp) | EOL); 58766550Snyan 58866550Snyan /* 58966550Snyan * The previous txp.tlink currently contains a pointer to 59066550Snyan * our txp | EOL. Want to clear the EOL, so write our 59166550Snyan * pointer to the previous txp. 59266550Snyan */ 59366550Snyan SWO(sc, sc->mtda[sc->mtd_prev].mtd_vtxp, sc->mtd_tlinko, 59466550Snyan LOWER(mtdp->mtd_vtxp)); 59566550Snyan 59666550Snyan /* make sure chip is running */ 59766550Snyan wbflush(); 59866550Snyan NIC_PUT(sc, SNCR_CR, CR_TXP); 59966550Snyan wbflush(); 60066550Snyan 601181298Sjhb /* 5 seconds to watch for failing to transmit */ 602181298Sjhb sc->sc_tx_timeout = 5; 603181298Sjhb 60466550Snyan return (totlen); 60566550Snyan} 60666550Snyan 60766550Snyan/* 60866550Snyan * These are called from sonicioctl() when /etc/ifconfig is run to set 60966550Snyan * the address or switch the i/f on. 61066550Snyan */ 61166550Snyan/* 61266550Snyan * CAM support 61366550Snyan */ 614179442Sjhbstatic void 615242871Snyancaminitialise(struct snc_softc *sc) 61666550Snyan{ 61766550Snyan u_int32_t v_cda = sc->v_cda; 61866550Snyan int i; 61966550Snyan int camoffset; 62066550Snyan 62166550Snyan for (i = 0; i < MAXCAM; i++) { 62266550Snyan camoffset = i * CDA_CAMDESC; 62366550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMEP), i); 62466550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMAP2), 0); 62566550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMAP1), 0); 62666550Snyan SWO(sc, v_cda, (camoffset + CDA_CAMAP0), 0); 62766550Snyan } 62866550Snyan SWO(sc, v_cda, CDA_ENABLE, 0); 62966550Snyan 63066550Snyan#ifdef SNCDEBUG 63166550Snyan if ((sncdebug & SNC_SHOWCAMENT) != 0) { 63266550Snyan camdump(sc); 63366550Snyan } 63466550Snyan#endif 63566550Snyan} 63666550Snyan 637179442Sjhbstatic void 638242871Snyancamentry(struct snc_softc *sc, int entry, u_char *ea) 63966550Snyan{ 64066550Snyan u_int32_t v_cda = sc->v_cda; 64166550Snyan int camoffset = entry * CDA_CAMDESC; 64266550Snyan 64366550Snyan SWO(sc, v_cda, camoffset + CDA_CAMEP, entry); 64466550Snyan SWO(sc, v_cda, camoffset + CDA_CAMAP2, (ea[5] << 8) | ea[4]); 64566550Snyan SWO(sc, v_cda, camoffset + CDA_CAMAP1, (ea[3] << 8) | ea[2]); 64666550Snyan SWO(sc, v_cda, camoffset + CDA_CAMAP0, (ea[1] << 8) | ea[0]); 64766550Snyan SWO(sc, v_cda, CDA_ENABLE, 64866550Snyan (SRO(sc, v_cda, CDA_ENABLE) | (1 << entry))); 64966550Snyan} 65066550Snyan 651179442Sjhbstatic void 652242871Snyancamprogram(struct snc_softc *sc) 65366550Snyan{ 65466550Snyan struct ifmultiaddr *ifma; 65566550Snyan struct ifnet *ifp; 65666550Snyan int timeout; 65766550Snyan int mcount = 0; 65866550Snyan 65966550Snyan caminitialise(sc); 66066550Snyan 661147256Sbrooks ifp = sc->sc_ifp; 66266550Snyan 66366550Snyan /* Always load our own address first. */ 664152315Sru camentry (sc, mcount, IF_LLADDR(sc->sc_ifp)); 66566550Snyan mcount++; 66666550Snyan 66766550Snyan /* Assume we won't need allmulti bit. */ 66866550Snyan ifp->if_flags &= ~IFF_ALLMULTI; 66966550Snyan 67066550Snyan /* Loop through multicast addresses */ 671195049Srwatson if_maddr_rlock(ifp); 67272084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 67366550Snyan if (ifma->ifma_addr->sa_family != AF_LINK) 67466550Snyan continue; 67566550Snyan if (mcount == MAXCAM) { 67666550Snyan ifp->if_flags |= IFF_ALLMULTI; 67766550Snyan break; 67866550Snyan } 67966550Snyan 68066550Snyan /* program the CAM with the specified entry */ 68166550Snyan camentry(sc, mcount, 68266550Snyan LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 68366550Snyan mcount++; 68466550Snyan } 685195049Srwatson if_maddr_runlock(ifp); 68666550Snyan 68766550Snyan NIC_PUT(sc, SNCR_CDP, LOWER(sc->v_cda)); 68866550Snyan NIC_PUT(sc, SNCR_CDC, MAXCAM); 68966550Snyan NIC_PUT(sc, SNCR_CR, CR_LCAM); 69066550Snyan wbflush(); 69166550Snyan 69266550Snyan timeout = 10000; 69366550Snyan while ((NIC_GET(sc, SNCR_CR) & CR_LCAM) && timeout--) 69466550Snyan continue; 69566550Snyan if (timeout == 0) { 69666550Snyan /* XXX */ 69766550Snyan panic("%s: CAM initialisation failed\n", 69866550Snyan device_get_nameunit(sc->sc_dev)); 69966550Snyan } 70066550Snyan timeout = 10000; 70166550Snyan while (((NIC_GET(sc, SNCR_ISR) & ISR_LCD) == 0) && timeout--) 70266550Snyan continue; 70366550Snyan 70466550Snyan if (NIC_GET(sc, SNCR_ISR) & ISR_LCD) 70566550Snyan NIC_PUT(sc, SNCR_ISR, ISR_LCD); 70666550Snyan else 70766550Snyan device_printf(sc->sc_dev, 70866550Snyan "CAM initialisation without interrupt\n"); 70966550Snyan} 71066550Snyan 71166550Snyan#ifdef SNCDEBUG 712179442Sjhbstatic void 713242871Snyancamdump(struct snc_softc *sc) 71466550Snyan{ 71566550Snyan int i; 71666550Snyan 71766550Snyan printf("CAM entries:\n"); 71866550Snyan NIC_PUT(sc, SNCR_CR, CR_RST); 71966550Snyan wbflush(); 72066550Snyan 72166550Snyan for (i = 0; i < 16; i++) { 722118607Sjhb u_short ap2, ap1, ap0; 72366550Snyan NIC_PUT(sc, SNCR_CEP, i); 72466550Snyan wbflush(); 72566550Snyan ap2 = NIC_GET(sc, SNCR_CAP2); 72666550Snyan ap1 = NIC_GET(sc, SNCR_CAP1); 72766550Snyan ap0 = NIC_GET(sc, SNCR_CAP0); 72866550Snyan printf("%d: ap2=0x%x ap1=0x%x ap0=0x%x\n", i, ap2, ap1, ap0); 72966550Snyan } 73066550Snyan printf("CAM enable 0x%x\n", NIC_GET(sc, SNCR_CEP)); 73166550Snyan 73266550Snyan NIC_PUT(sc, SNCR_CR, 0); 73366550Snyan wbflush(); 73466550Snyan} 73566550Snyan#endif 73666550Snyan 737179442Sjhbstatic void 738242871Snyaninitialise_tda(struct snc_softc *sc) 73966550Snyan{ 74066550Snyan struct mtd *mtd; 74166550Snyan int i; 74266550Snyan 74366550Snyan for (i = 0; i < NTDA; i++) { 74466550Snyan mtd = &sc->mtda[i]; 74566550Snyan mtd->mtd_mbuf = 0; 74666550Snyan } 74766550Snyan 74866550Snyan sc->mtd_hw = 0; 74966550Snyan sc->mtd_prev = NTDA - 1; 75066550Snyan sc->mtd_free = 0; 75166550Snyan sc->mtd_tlinko = TXP_FRAGOFF + 1*TXP_FRAGSIZE + TXP_FPTRLO; 75266550Snyan sc->mtd_pint = NTDA/2; 75366550Snyan 75466550Snyan NIC_PUT(sc, SNCR_UTDA, UPPER(sc->mtda[0].mtd_vtxp)); 75566550Snyan NIC_PUT(sc, SNCR_CTDA, LOWER(sc->mtda[0].mtd_vtxp)); 75666550Snyan} 75766550Snyan 758179442Sjhbstatic void 759242871Snyaninitialise_rda(struct snc_softc *sc) 76066550Snyan{ 76166550Snyan int i; 76266550Snyan u_int32_t vv_rda = 0; 76366550Snyan u_int32_t v_rda = 0; 76466550Snyan 76566550Snyan /* link the RDA's together into a circular list */ 76666550Snyan for (i = 0; i < (sc->sc_nrda - 1); i++) { 76766550Snyan v_rda = sc->v_rda + (i * RXPKT_SIZE(sc)); 76866550Snyan vv_rda = sc->v_rda + ((i+1) * RXPKT_SIZE(sc)); 76966550Snyan SWO(sc, v_rda, RXPKT_RLINK, LOWER(vv_rda)); 77066550Snyan SWO(sc, v_rda, RXPKT_INUSE, 1); 77166550Snyan } 77266550Snyan v_rda = sc->v_rda + ((sc->sc_nrda - 1) * RXPKT_SIZE(sc)); 77366550Snyan SWO(sc, v_rda, RXPKT_RLINK, LOWER(sc->v_rda) | EOL); 77466550Snyan SWO(sc, v_rda, RXPKT_INUSE, 1); 77566550Snyan 77666550Snyan /* mark end of receive descriptor list */ 77766550Snyan sc->sc_rdamark = sc->sc_nrda - 1; 77866550Snyan 77966550Snyan sc->sc_rxmark = 0; 78066550Snyan 78166550Snyan NIC_PUT(sc, SNCR_URDA, UPPER(sc->v_rda)); 78266550Snyan NIC_PUT(sc, SNCR_CRDA, LOWER(sc->v_rda)); 78366550Snyan wbflush(); 78466550Snyan} 78566550Snyan 786179442Sjhbstatic void 787242871Snyaninitialise_rra(struct snc_softc *sc) 78866550Snyan{ 78966550Snyan int i; 79066550Snyan u_int v; 79166550Snyan int bitmode = sc->bitmode; 79266550Snyan 79366550Snyan if (bitmode) 79466550Snyan NIC_PUT(sc, SNCR_EOBC, RBASIZE(sc) / 2 - 2); 79566550Snyan else 79666550Snyan NIC_PUT(sc, SNCR_EOBC, RBASIZE(sc) / 2 - 1); 79766550Snyan 79866550Snyan NIC_PUT(sc, SNCR_URRA, UPPER(sc->v_rra[0])); 79966550Snyan NIC_PUT(sc, SNCR_RSA, LOWER(sc->v_rra[0])); 80066550Snyan /* rea must point just past the end of the rra space */ 80166550Snyan NIC_PUT(sc, SNCR_REA, LOWER(sc->v_rea)); 80266550Snyan NIC_PUT(sc, SNCR_RRP, LOWER(sc->v_rra[0])); 80366550Snyan NIC_PUT(sc, SNCR_RSC, 0); 80466550Snyan 80566550Snyan /* fill up SOME of the rra with buffers */ 80666550Snyan for (i = 0; i < NRBA; i++) { 80766550Snyan v = SONIC_GETDMA(sc->rbuf[i]); 80866550Snyan SWO(sc, sc->v_rra[i], RXRSRC_PTRHI, UPPER(v)); 80966550Snyan SWO(sc, sc->v_rra[i], RXRSRC_PTRLO, LOWER(v)); 810179442Sjhb SWO(sc, sc->v_rra[i], RXRSRC_WCHI, UPPER(PAGE_SIZE/2)); 811179442Sjhb SWO(sc, sc->v_rra[i], RXRSRC_WCLO, LOWER(PAGE_SIZE/2)); 81266550Snyan } 81366550Snyan sc->sc_rramark = NRBA; 81466550Snyan NIC_PUT(sc, SNCR_RWP, LOWER(sc->v_rra[sc->sc_rramark])); 81566550Snyan wbflush(); 81666550Snyan} 81766550Snyan 81866550Snyanvoid 819242871Snyansncintr(void *arg) 82066550Snyan{ 82166550Snyan struct snc_softc *sc = (struct snc_softc *)arg; 82266550Snyan int isr; 82366550Snyan 82466550Snyan if (sc->sc_enabled == 0) 82566550Snyan return; 82666550Snyan 827181298Sjhb SNC_LOCK(sc); 82866550Snyan while ((isr = (NIC_GET(sc, SNCR_ISR) & ISR_ALL)) != 0) { 82966550Snyan /* scrub the interrupts that we are going to service */ 83066550Snyan NIC_PUT(sc, SNCR_ISR, isr); 83166550Snyan wbflush(); 83266550Snyan 83366550Snyan if (isr & (ISR_BR | ISR_LCD | ISR_TC)) 83466550Snyan device_printf(sc->sc_dev, 83566550Snyan "unexpected interrupt status 0x%x\n", 83666550Snyan isr); 83766550Snyan 83866550Snyan if (isr & (ISR_TXDN | ISR_TXER | ISR_PINT)) 83966550Snyan sonictxint(sc); 84066550Snyan 84166550Snyan if (isr & ISR_PKTRX) 84266550Snyan sonicrxint(sc); 84366550Snyan 84466550Snyan if (isr & (ISR_HBL | ISR_RDE | ISR_RBE | ISR_RBAE | ISR_RFO)) { 84566550Snyan if (isr & ISR_HBL) 84666550Snyan /* 84766550Snyan * The repeater is not providing a heartbeat. 84866550Snyan * In itself this isn't harmful, lots of the 84966550Snyan * cheap repeater hubs don't supply a heartbeat. 85066550Snyan * So ignore the lack of heartbeat. Its only 85166550Snyan * if we can't detect a carrier that we have a 85266550Snyan * problem. 85366550Snyan */ 85466550Snyan ; 85566550Snyan if (isr & ISR_RDE) 85666550Snyan device_printf(sc->sc_dev, 85766550Snyan "receive descriptors exhausted\n"); 85866550Snyan if (isr & ISR_RBE) 85966550Snyan device_printf(sc->sc_dev, 86066550Snyan "receive buffers exhausted\n"); 86166550Snyan if (isr & ISR_RBAE) 86266550Snyan device_printf(sc->sc_dev, 86366550Snyan "receive buffer area exhausted\n"); 86466550Snyan if (isr & ISR_RFO) 86566550Snyan device_printf(sc->sc_dev, 86666550Snyan "receive FIFO overrun\n"); 86766550Snyan } 86866550Snyan if (isr & (ISR_CRC | ISR_FAE | ISR_MP)) { 86966550Snyan#ifdef notdef 87066550Snyan if (isr & ISR_CRC) 87166550Snyan sc->sc_crctally++; 87266550Snyan if (isr & ISR_FAE) 87366550Snyan sc->sc_faetally++; 87466550Snyan if (isr & ISR_MP) 87566550Snyan sc->sc_mptally++; 87666550Snyan#endif 87766550Snyan } 878181298Sjhb sncstart_locked(sc->sc_ifp); 87966550Snyan } 880181298Sjhb SNC_UNLOCK(sc); 88166550Snyan return; 88266550Snyan} 88366550Snyan 88466550Snyan/* 88566550Snyan * Transmit interrupt routine 88666550Snyan */ 887179442Sjhbstatic void 888242871Snyansonictxint(struct snc_softc *sc) 88966550Snyan{ 89066550Snyan struct mtd *mtd; 89166550Snyan u_int32_t txp; 89266550Snyan unsigned short txp_status; 89366550Snyan int mtd_hw; 894147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 89566550Snyan 89666550Snyan mtd_hw = sc->mtd_hw; 89766550Snyan 89866550Snyan if (mtd_hw == sc->mtd_free) 89966550Snyan return; 90066550Snyan 90166550Snyan while (mtd_hw != sc->mtd_free) { 90266550Snyan mtd = &sc->mtda[mtd_hw]; 90366550Snyan 90466550Snyan txp = mtd->mtd_vtxp; 90566550Snyan 90666550Snyan if (SRO(sc, txp, TXP_STATUS) == 0) { 90766550Snyan break; /* it hasn't really gone yet */ 90866550Snyan } 90966550Snyan 91066550Snyan#ifdef SNCDEBUG 91166550Snyan if ((sncdebug & SNC_SHOWTXHDR) != 0) 91266550Snyan { 91366550Snyan struct ether_header eh; 91466550Snyan 91566550Snyan (*sc->sc_copyfrombuf)(sc, &eh, mtd->mtd_vbuf, sizeof(eh)); 91666550Snyan device_printf(sc->sc_dev, 91766550Snyan "xmit status=0x%x len=%d type=0x%x from %6D", 91866550Snyan SRO(sc, txp, TXP_STATUS), 91966550Snyan SRO(sc, txp, TXP_PKTSIZE), 92066550Snyan htons(eh.ether_type), 92166550Snyan eh.ether_shost, ":"); 92266550Snyan printf(" (to %6D)\n", eh.ether_dhost, ":"); 92366550Snyan } 92466550Snyan#endif /* SNCDEBUG */ 92566550Snyan 926181298Sjhb sc->sc_tx_timeout = 0; 927148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 92866550Snyan 92966550Snyan if (mtd->mtd_mbuf != 0) { 93066550Snyan m_freem(mtd->mtd_mbuf); 93166550Snyan mtd->mtd_mbuf = 0; 93266550Snyan } 93366550Snyan if (++mtd_hw == NTDA) mtd_hw = 0; 93466550Snyan 93566550Snyan txp_status = SRO(sc, txp, TXP_STATUS); 93666550Snyan 93766550Snyan ifp->if_collisions += (txp_status & TCR_EXC) ? 16 : 93866550Snyan ((txp_status & TCR_NC) >> 12); 93966550Snyan 94066550Snyan if ((txp_status & TCR_PTX) == 0) { 94166550Snyan ifp->if_oerrors++; 94266550Snyan device_printf(sc->sc_dev, "Tx packet status=0x%x\n", 94366550Snyan txp_status); 94466550Snyan 94566550Snyan /* XXX - DG This looks bogus */ 94666550Snyan if (mtd_hw != sc->mtd_free) { 94766550Snyan printf("resubmitting remaining packets\n"); 94866550Snyan mtd = &sc->mtda[mtd_hw]; 94966550Snyan NIC_PUT(sc, SNCR_CTDA, LOWER(mtd->mtd_vtxp)); 95066550Snyan NIC_PUT(sc, SNCR_CR, CR_TXP); 95166550Snyan wbflush(); 95266550Snyan break; 95366550Snyan } 95466550Snyan } 95566550Snyan } 95666550Snyan 95766550Snyan sc->mtd_hw = mtd_hw; 95866550Snyan return; 95966550Snyan} 96066550Snyan 96166550Snyan/* 96266550Snyan * Receive interrupt routine 96366550Snyan */ 964179442Sjhbstatic void 965242871Snyansonicrxint(struct snc_softc *sc) 96666550Snyan{ 96766550Snyan u_int32_t rda; 96866550Snyan int orra; 96966550Snyan int len; 97066550Snyan int rramark; 97166550Snyan int rdamark; 97266550Snyan u_int16_t rxpkt_ptr; 97366550Snyan 97466550Snyan rda = sc->v_rda + (sc->sc_rxmark * RXPKT_SIZE(sc)); 97566550Snyan 97666550Snyan while (SRO(sc, rda, RXPKT_INUSE) == 0) { 97766550Snyan u_int status = SRO(sc, rda, RXPKT_STATUS); 97866550Snyan 97966550Snyan orra = RBASEQ(SRO(sc, rda, RXPKT_SEQNO)) & RRAMASK; 98066550Snyan rxpkt_ptr = SRO(sc, rda, RXPKT_PTRLO); 98166550Snyan /* 98266550Snyan * Do not trunc ether_header length. 98366550Snyan * Our sonic_read() and sonic_get() require it. 98466550Snyan */ 98566550Snyan len = SRO(sc, rda, RXPKT_BYTEC) - FCSSIZE; 98666550Snyan if (status & RCR_PRX) { 987179442Sjhb /* XXX: Does PAGE_MASK require? */ 98866550Snyan u_int32_t pkt = 989179442Sjhb sc->rbuf[orra & RBAMASK] + (rxpkt_ptr & PAGE_MASK); 99066550Snyan if (sonic_read(sc, pkt, len)) 991147256Sbrooks sc->sc_ifp->if_ipackets++; 99266550Snyan else 993147256Sbrooks sc->sc_ifp->if_ierrors++; 99466550Snyan } else 995147256Sbrooks sc->sc_ifp->if_ierrors++; 99666550Snyan 99766550Snyan /* 99866550Snyan * give receive buffer area back to chip. 99966550Snyan * 100066550Snyan * If this was the last packet in the RRA, give the RRA to 100166550Snyan * the chip again. 100266550Snyan * If sonic read didnt copy it out then we would have to 100366550Snyan * wait !! 100466550Snyan * (dont bother add it back in again straight away) 100566550Snyan * 100666550Snyan * Really, we're doing v_rra[rramark] = v_rra[orra] but 100766550Snyan * we have to use the macros because SONIC might be in 100866550Snyan * 16 or 32 bit mode. 100966550Snyan */ 101066550Snyan if (status & RCR_LPKT) { 101166550Snyan u_int32_t tmp1, tmp2; 101266550Snyan 101366550Snyan rramark = sc->sc_rramark; 101466550Snyan tmp1 = sc->v_rra[rramark]; 101566550Snyan tmp2 = sc->v_rra[orra]; 101666550Snyan SWO(sc, tmp1, RXRSRC_PTRLO, 101766550Snyan SRO(sc, tmp2, RXRSRC_PTRLO)); 101866550Snyan SWO(sc, tmp1, RXRSRC_PTRHI, 101966550Snyan SRO(sc, tmp2, RXRSRC_PTRHI)); 102066550Snyan SWO(sc, tmp1, RXRSRC_WCLO, 102166550Snyan SRO(sc, tmp2, RXRSRC_WCLO)); 102266550Snyan SWO(sc, tmp1, RXRSRC_WCHI, 102366550Snyan SRO(sc, tmp2, RXRSRC_WCHI)); 102466550Snyan 102566550Snyan /* zap old rra for fun */ 102666550Snyan SWO(sc, tmp2, RXRSRC_WCHI, 0); 102766550Snyan SWO(sc, tmp2, RXRSRC_WCLO, 0); 102866550Snyan 102966550Snyan sc->sc_rramark = (++rramark) & RRAMASK; 103066550Snyan NIC_PUT(sc, SNCR_RWP, LOWER(sc->v_rra[rramark])); 103166550Snyan wbflush(); 103266550Snyan } 103366550Snyan 103466550Snyan /* 103566550Snyan * give receive descriptor back to chip simple 103666550Snyan * list is circular 103766550Snyan */ 103866550Snyan rdamark = sc->sc_rdamark; 103966550Snyan SWO(sc, rda, RXPKT_INUSE, 1); 104066550Snyan SWO(sc, rda, RXPKT_RLINK, 104166550Snyan SRO(sc, rda, RXPKT_RLINK) | EOL); 104266550Snyan SWO(sc, (sc->v_rda + (rdamark * RXPKT_SIZE(sc))), RXPKT_RLINK, 104366550Snyan SRO(sc, (sc->v_rda + (rdamark * RXPKT_SIZE(sc))), 104466550Snyan RXPKT_RLINK) & ~EOL); 104566550Snyan sc->sc_rdamark = sc->sc_rxmark; 104666550Snyan 104766550Snyan if (++sc->sc_rxmark >= sc->sc_nrda) 104866550Snyan sc->sc_rxmark = 0; 104966550Snyan rda = sc->v_rda + (sc->sc_rxmark * RXPKT_SIZE(sc)); 105066550Snyan } 105166550Snyan} 105266550Snyan 105366550Snyan/* 105466550Snyan * sonic_read -- pull packet off interface and forward to 105566550Snyan * appropriate protocol handler 105666550Snyan */ 1057179442Sjhbstatic int 1058242871Snyansonic_read(struct snc_softc *sc, u_int32_t pkt, int len) 105966550Snyan{ 1060147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 106166550Snyan struct ether_header *et; 106266550Snyan struct mbuf *m; 106366550Snyan 106466550Snyan if (len <= sizeof(struct ether_header) || 106566550Snyan len > ETHERMTU + sizeof(struct ether_header)) { 106666550Snyan device_printf(sc->sc_dev, 106766550Snyan "invalid packet length %d bytes\n", len); 106866550Snyan return (0); 106966550Snyan } 107066550Snyan 107166550Snyan /* Pull packet off interface. */ 107266550Snyan m = sonic_get(sc, pkt, len); 107366550Snyan if (m == 0) { 107466550Snyan return (0); 107566550Snyan } 107666550Snyan 107766550Snyan /* We assume that the header fit entirely in one mbuf. */ 107866550Snyan et = mtod(m, struct ether_header *); 107966550Snyan 108066550Snyan#ifdef SNCDEBUG 108166550Snyan if ((sncdebug & SNC_SHOWRXHDR) != 0) 108266550Snyan { 108366550Snyan device_printf(sc->sc_dev, "rcvd 0x%x len=%d type=0x%x from %6D", 108466550Snyan pkt, len, htons(et->ether_type), 108566550Snyan et->ether_shost, ":"); 108666550Snyan printf(" (to %6D)\n", et->ether_dhost, ":"); 108766550Snyan } 108866550Snyan#endif /* SNCDEBUG */ 108966550Snyan 1090106937Ssam /* Pass the packet up. */ 1091181298Sjhb SNC_UNLOCK(sc); 1092106937Ssam (*ifp->if_input)(ifp, m); 1093181298Sjhb SNC_LOCK(sc); 109466550Snyan return (1); 109566550Snyan} 109666550Snyan 109766550Snyan 109866550Snyan/* 109966550Snyan * munge the received packet into an mbuf chain 110066550Snyan */ 1101179442Sjhbstatic struct mbuf * 1102242871Snyansonic_get(struct snc_softc *sc, u_int32_t pkt, int datalen) 110366550Snyan{ 110466550Snyan struct mbuf *m, *top, **mp; 110566550Snyan int len; 110666550Snyan /* 110766550Snyan * Do not trunc ether_header length. 110866550Snyan * Our sonic_read() and sonic_get() require it. 110966550Snyan */ 111066550Snyan 1111243857Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 111266550Snyan if (m == 0) 111366550Snyan return (0); 1114147256Sbrooks m->m_pkthdr.rcvif = sc->sc_ifp; 111566550Snyan m->m_pkthdr.len = datalen; 111666550Snyan len = MHLEN; 111766550Snyan top = 0; 111866550Snyan mp = ⊤ 111966550Snyan 112066550Snyan while (datalen > 0) { 112166550Snyan if (top) { 1122243857Sglebius MGET(m, M_NOWAIT, MT_DATA); 112366550Snyan if (m == 0) { 112466550Snyan m_freem(top); 112566550Snyan return (0); 112666550Snyan } 112766550Snyan len = MLEN; 112866550Snyan } 112966550Snyan if (datalen >= MINCLSIZE) { 1130243857Sglebius MCLGET(m, M_NOWAIT); 113166550Snyan if ((m->m_flags & M_EXT) == 0) { 113266550Snyan if (top) m_freem(top); 113366550Snyan return (0); 113466550Snyan } 113566550Snyan len = MCLBYTES; 113666550Snyan } 113766550Snyan#if 0 113866550Snyan /* XXX: Require? */ 113966550Snyan if (!top) { 114066550Snyan register int pad = 114166550Snyan ALIGN(sizeof(struct ether_header)) - 114266550Snyan sizeof(struct ether_header); 114366550Snyan m->m_data += pad; 114466550Snyan len -= pad; 114566550Snyan } 114666550Snyan#endif 114766550Snyan m->m_len = len = min(datalen, len); 114866550Snyan 114966550Snyan (*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), pkt, len); 115066550Snyan pkt += len; 115166550Snyan datalen -= len; 115266550Snyan *mp = m; 115366550Snyan mp = &m->m_next; 115466550Snyan } 115566550Snyan 115666550Snyan return (top); 115766550Snyan} 115866550Snyan/* 115966550Snyan * Enable power on the interface. 116066550Snyan */ 116166550Snyanint 1162242871Snyansnc_enable(struct snc_softc *sc) 116366550Snyan{ 116466550Snyan 116566550Snyan#ifdef SNCDEBUG 116666550Snyan device_printf(sc->sc_dev, "snc_enable()\n"); 116766550Snyan#endif /* SNCDEBUG */ 116866550Snyan 116966550Snyan if (sc->sc_enabled == 0 && sc->sc_enable != NULL) { 117066550Snyan if ((*sc->sc_enable)(sc) != 0) { 117166550Snyan device_printf(sc->sc_dev, "device enable failed\n"); 117266550Snyan return (EIO); 117366550Snyan } 117466550Snyan } 117566550Snyan 117666550Snyan sc->sc_enabled = 1; 117766550Snyan return (0); 117866550Snyan} 117966550Snyan 118066550Snyan/* 118166550Snyan * Disable power on the interface. 118266550Snyan */ 118366550Snyanvoid 1184242871Snyansnc_disable(struct snc_softc *sc) 118566550Snyan{ 118666550Snyan 118766550Snyan#ifdef SNCDEBUG 118866550Snyan device_printf(sc->sc_dev, "snc_disable()\n"); 118966550Snyan#endif /* SNCDEBUG */ 119066550Snyan 119166550Snyan if (sc->sc_enabled != 0 && sc->sc_disable != NULL) { 119266550Snyan (*sc->sc_disable)(sc); 119366550Snyan sc->sc_enabled = 0; 119466550Snyan } 119566550Snyan} 119666550Snyan 119766550Snyan 1198