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 = &top;
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