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