Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 10 May 2004 07:00:44 -0700 (PDT)
From:      Jianqin Qu <jqu@its.brooklyn.cuny.edu>
To:        freebsd-i386@FreeBSD.org
Subject:   Re: i386/64656: Bugs with Ethernet driver "bfe"[patch] (fwd)
Message-ID:  <200405101400.i4AE0is9064715@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR i386/64656; it has been noted by GNATS.

From: Jianqin Qu <jqu@its.brooklyn.cuny.edu>
To: freebsd-gnats-submit@freebsd.org, jqu@its.brooklyn.cuny.edu
Cc:  
Subject: Re: i386/64656: Bugs with Ethernet driver "bfe"[patch] (fwd)
Date: Mon, 10 May 2004 09:50:48 -0400 (EDT)

   This message is in MIME format.  The first part should be readable text,
   while the remaining parts are likely unreadable without MIME-aware tools.
   Send mail to mime@docserver.cac.washington.edu for more info.
 
 --------------090505010100030000050608
 Content-Type: TEXT/PLAIN; CHARSET=US-ASCII; FORMAT=flowed
 Content-ID: <Pine.GSO.4.58.0405100938532.27550@atrium69.its.brooklyn.cuny.edu>
 
 
 More tests showed that this problem does not appear every time at boot
 on my Dell Optiplex 160L.
 
 I found a tempory workaround for that.  When the problem occurs, simply
 shut down your computer completely (not just switch off the power button
 on your computer, but also detach your power cable!!! )   and  wait for
 sufficiently long time  and  then restart your computer to boot into
 FreeBSD 5.2.1 directly.   Here sufficiently long time varies, but 30
 minutes seems sufficient for my case most of time.
 
 If  you switch off your computer power button without unplugging the
 power cable and the LED light on the system board is on, then after some
 time (several hours) when you boot your computer into FreeBSD 5.2.1,
 this problem almost certainly occur.
 
 Rebooting into FreeBSD 5.2.1 release from another operating system
 installed on the same box  may reproduce the problem too.  Particularly
 rebooting into FreeBSD from a Linux system with a NIC driver of version
 3.0.7 from BroadCom  always results in the occurence of this problem.
 When the problem occurs, the link and activity indicator of the NIC are
 both off.   So it seems the bfe driver fails to reset the NIC card to a
 correct state in some cases.
 
 A patch with the bfe driver  is attached, which fixes the bug.  The
 patch is against 5.2.1 Release and had been tested with it.   The
 patched file "if_bfe.c" is also attached for reference.
 
 Jianqin
 --------------090505010100030000050608
 Content-Type: TEXT/PLAIN; NAME="tcp52.patch"
 Content-ID: <Pine.GSO.4.58.0405100938533.27550@atrium69.its.brooklyn.cuny.edu>
 Content-Description: 
 Content-Disposition: INLINE; FILENAME="tcp52.patch"
 
 ? tcp_reass-5.2.1-20040301.patch
 Index: tcp_input.c
 ===================================================================
 RCS file: /home/ncvs/src/sys/netinet/tcp_input.c,v
 retrieving revision 1.217.2.1
 diff -u -p -r1.217.2.1 tcp_input.c
 --- sys/netinet/tcp_input.c	9 Jan 2004 12:32:36 -0000	1.217.2.1
 +++ sys/netinet/tcp_input.c	1 Mar 2004 15:18:54 -0000
 @@ -57,6 +57,8 @@
  
  #include <machine/cpu.h>	/* before tcp_seq.h, for tcp_random18() */
  
 +#include <vm/uma.h>
 +
  #include <net/if.h>
  #include <net/route.h>
  
 @@ -97,8 +99,6 @@
  
  #include <machine/in_cksum.h>
  
 -MALLOC_DEFINE(M_TSEGQ, "tseg_qent", "TCP segment queue entry");
 -
  static const int tcprexmtthresh = 3;
  tcp_cc	tcp_ccgen;
  
 @@ -134,6 +134,24 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, rfc3
      &tcp_do_rfc3390, 0,
      "Enable RFC 3390 (Increasing TCP's Initial Congestion Window)");
  
 +SYSCTL_NODE(_net_inet_tcp, OID_AUTO, reass, CTLFLAG_RW, 0,
 +    "TCP Segment Reassembly Queue");
 +
 +static int tcp_reass_maxseg = 0;
 +SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, maxsegments, CTLFLAG_RDTUN,
 +    &tcp_reass_maxseg, 0,
 +    "Global maximum number of TCP Segments in Reassembly Queue");
 +
 +int tcp_reass_qsize = 0;
 +SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, cursegments, CTLFLAG_RD,
 +    &tcp_reass_qsize, 0,
 +    "Global number of TCP Segments currently in Reassembly Queue");
 +
 +static int tcp_reass_overflows = 0;
 +SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, overflows, CTLFLAG_RD,
 +    &tcp_reass_overflows, 0,
 +    "Global number of TCP Segment Reassembly Queue Overflows");
 +
  struct inpcbhead tcb;
  #define	tcb6	tcb  /* for KAME src sync over BSD*'s */
  struct inpcbinfo tcbinfo;
 @@ -174,6 +192,19 @@ do { \
  	    (tp->t_flags & TF_RXWIN0SENT) == 0) &&			\
  	    (tcp_delack_enabled || (tp->t_flags & TF_NEEDSYN)))
  
 +/* Initialize TCP reassembly queue */
 +uma_zone_t	tcp_reass_zone;
 +void
 +tcp_reass_init()
 +{
 +	tcp_reass_maxseg = nmbclusters / 16;
 +	TUNABLE_INT_FETCH("net.inet.tcp.reass.maxsegments",
 +	    &tcp_reass_maxseg);
 +	tcp_reass_zone = uma_zcreate("tcpreass", sizeof (struct tseg_qent),
 +	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
 +	uma_zone_set_max(tcp_reass_zone, tcp_reass_maxseg);
 +}
 +
  static int
  tcp_reass(tp, th, tlenp, m)
  	register struct tcpcb *tp;
 @@ -184,7 +215,7 @@ tcp_reass(tp, th, tlenp, m)
  	struct tseg_qent *q;
  	struct tseg_qent *p = NULL;
  	struct tseg_qent *nq;
 -	struct tseg_qent *te;
 +	struct tseg_qent *te = NULL;
  	struct socket *so = tp->t_inpcb->inp_socket;
  	int flags;
  
 @@ -195,9 +226,27 @@ tcp_reass(tp, th, tlenp, m)
  	if (th == 0)
  		goto present;
  
 -	/* Allocate a new queue entry. If we can't, just drop the pkt. XXX */
 -	MALLOC(te, struct tseg_qent *, sizeof (struct tseg_qent), M_TSEGQ,
 -	       M_NOWAIT);
 +	/*
 +	 * Limit the number of segments in the reassembly queue to prevent
 +	 * holding on to too many segments (and thus running out of mbufs).
 +	 * Make sure to let the missing segment through which caused this
 +	 * queue.  Always keep one global queue entry spare to be able to
 +	 * process the missing segment.
 +	 */
 +	if (th->th_seq != tp->rcv_nxt &&
 +	    tcp_reass_qsize + 1 >= tcp_reass_maxseg) {
 +		tcp_reass_overflows++;
 +		tcpstat.tcps_rcvmemdrop++;
 +		m_freem(m);
 +		return (0);
 +	}
 +	tcp_reass_qsize++;
 +
 +	/*
 +	 * Allocate a new queue entry. If we can't, or hit the zone limit
 +	 * just drop the pkt.
 +	 */
 +	te = uma_zalloc(tcp_reass_zone, M_NOWAIT);
  	if (te == NULL) {
  		tcpstat.tcps_rcvmemdrop++;
  		m_freem(m);
 @@ -227,7 +276,8 @@ tcp_reass(tp, th, tlenp, m)
  				tcpstat.tcps_rcvduppack++;
  				tcpstat.tcps_rcvdupbyte += *tlenp;
  				m_freem(m);
 -				FREE(te, M_TSEGQ);
 +				uma_zfree(tcp_reass_zone, te);
 +				tcp_reass_qsize--;
  				/*
  				 * Try to present any queued data
  				 * at the left window edge to the user.
 @@ -262,7 +312,8 @@ tcp_reass(tp, th, tlenp, m)
  		nq = LIST_NEXT(q, tqe_q);
  		LIST_REMOVE(q, tqe_q);
  		m_freem(q->tqe_m);
 -		FREE(q, M_TSEGQ);
 +		uma_zfree(tcp_reass_zone, q);
 +		tcp_reass_qsize--;
  		q = nq;
  	}
  
 @@ -296,7 +347,8 @@ present:
  			m_freem(q->tqe_m);
  		else
  			sbappendstream(&so->so_rcv, q->tqe_m);
 -		FREE(q, M_TSEGQ);
 +		uma_zfree(tcp_reass_zone, q);
 +		tcp_reass_qsize--;
  		q = nq;
  	} while (q && q->tqe_th->th_seq == tp->rcv_nxt);
  	ND6_HINT(tp);
 Index: tcp_subr.c
 ===================================================================
 RCS file: /home/ncvs/src/sys/netinet/tcp_subr.c,v
 retrieving revision 1.169.2.3
 diff -u -p -r1.169.2.3 tcp_subr.c
 --- sys/netinet/tcp_subr.c	23 Feb 2004 15:32:55 -0000	1.169.2.3
 +++ sys/netinet/tcp_subr.c	1 Mar 2004 15:18:54 -0000
 @@ -286,6 +286,7 @@ tcp_init()
  	tcp_timer_init();
  	syncache_init();
  	tcp_hc_init();
 +	tcp_reass_init();
  }
  
  /*
 @@ -708,7 +709,8 @@ tcp_discardcb(tp)
  	while ((q = LIST_FIRST(&tp->t_segq)) != NULL) {
  		LIST_REMOVE(q, tqe_q);
  		m_freem(q->tqe_m);
 -		FREE(q, M_TSEGQ);
 +		uma_zfree(tcp_reass_zone, q);
 +		tcp_reass_qsize--;
  	}
  	inp->inp_ppcb = NULL;
  	tp->t_inpcb = NULL;
 @@ -769,7 +771,8 @@ tcp_drain()
  			            != NULL) {
  					LIST_REMOVE(te, tqe_q);
  					m_freem(te->tqe_m);
 -					FREE(te, M_TSEGQ);
 +					uma_zfree(tcp_reass_zone, te);
 +					tcp_reass_qsize--;
  				}
  			}
  			INP_UNLOCK(inpb);
 Index: tcp_var.h
 ===================================================================
 RCS file: /home/ncvs/src/sys/netinet/tcp_var.h,v
 retrieving revision 1.93.2.1
 diff -u -p -r1.93.2.1 tcp_var.h
 --- sys/netinet/tcp_var.h	9 Jan 2004 12:32:36 -0000	1.93.2.1
 +++ sys/netinet/tcp_var.h	1 Mar 2004 15:18:55 -0000
 @@ -54,9 +54,8 @@ struct tseg_qent {
  	struct	mbuf	*tqe_m;		/* mbuf contains packet */
  };
  LIST_HEAD(tsegqe_head, tseg_qent);
 -#ifdef MALLOC_DECLARE
 -MALLOC_DECLARE(M_TSEGQ);
 -#endif
 +extern int	tcp_reass_qsize;
 +extern struct uma_zone	*tcp_reass_zone;
  
  struct tcptemp {
  	u_char	tt_ipgen[40]; /* the size must be of max ip header, now IPv6 */
 @@ -514,6 +513,7 @@ struct tcpcb *
  int	 tcp_output(struct tcpcb *);
  struct inpcb *
  	 tcp_quench(struct inpcb *, int);
 +void	 tcp_reass_init(void);
  void	 tcp_respond(struct tcpcb *, void *,
  	    struct tcphdr *, struct mbuf *, tcp_seq, tcp_seq, int);
  int	 tcp_twrespond(struct tcptw *, struct socket *, struct mbuf *, int);
 
 --------------090505010100030000050608
 Content-Type: TEXT/PLAIN; NAME="if_bfe.c"
 Content-ID: <Pine.GSO.4.58.0405100938534.27550@atrium69.its.brooklyn.cuny.edu>
 Content-Description: 
 Content-Disposition: INLINE; FILENAME="if_bfe.c"
 
 /*
  * Copyright (c) 2003 Stuart Walsh<stu@ipng.org.uk>
  * and Duncan Barclay<dmlb@dmlb.org>
  */
 
 /*
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 'AS IS' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/sys/dev/bfe/if_bfe.c,v 1.4 2003/11/14 19:00:30 sam Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/sockio.h>
 #include <sys/mbuf.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
 #include <sys/socket.h>
 #include <sys/queue.h>
 
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <net/ethernet.h>
 #include <net/if_dl.h>
 #include <net/if_media.h>
 
 #include <net/bpf.h>
 
 #include <net/if_types.h>
 #include <net/if_vlan_var.h>
 
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 
 #include <machine/clock.h>      /* for DELAY */
 #include <machine/bus_memio.h>
 #include <machine/bus.h>
 #include <machine/resource.h>
 #include <sys/bus.h>
 #include <sys/rman.h>
 
 #include <dev/mii/mii.h>
 #include <dev/mii/miivar.h>
 #include "miidevs.h"
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
 
 #include <dev/bfe/if_bfereg.h>
 
 MODULE_DEPEND(bfe, pci, 1, 1, 1);
 MODULE_DEPEND(bfe, ether, 1, 1, 1);
 MODULE_DEPEND(bfe, miibus, 1, 1, 1);
 
 /* "controller miibus0" required.  See GENERIC if you get errors here. */
 #include "miibus_if.h"
 
 #define BFE_DEVDESC_MAX		64	/* Maximum device description length */
 
 static struct bfe_type bfe_devs[] = {
 	{ BCOM_VENDORID, BCOM_DEVICEID_BCM4401,
 		"Broadcom BCM4401 Fast Ethernet" },
 		{ 0, 0, NULL }
 };
 
 static int  bfe_probe				(device_t);
 static int  bfe_attach				(device_t);
 static int  bfe_detach				(device_t);
 static void bfe_release_resources	(struct bfe_softc *);
 static void bfe_intr				(void *);
 static void bfe_start				(struct ifnet *);
 static int  bfe_ioctl				(struct ifnet *, u_long, caddr_t);
 static void bfe_init				(void *);
 static void bfe_stop				(struct bfe_softc *);
 static void bfe_watchdog			(struct ifnet *);
 static void bfe_shutdown			(device_t);
 static void bfe_tick				(void *);
 static void bfe_txeof				(struct bfe_softc *);
 static void bfe_rxeof				(struct bfe_softc *);
 static void bfe_set_rx_mode			(struct bfe_softc *);
 static int  bfe_list_rx_init		(struct bfe_softc *);
 static int  bfe_list_newbuf			(struct bfe_softc *, int, struct mbuf*);
 static void bfe_rx_ring_free		(struct bfe_softc *);
 
 static void bfe_pci_setup			(struct bfe_softc *, u_int32_t);
 static int  bfe_ifmedia_upd			(struct ifnet *);
 static void bfe_ifmedia_sts			(struct ifnet *, struct ifmediareq *);
 static int  bfe_miibus_readreg		(device_t, int, int);
 static int  bfe_miibus_writereg		(device_t, int, int, int);
 static void bfe_miibus_statchg		(device_t);
 static int  bfe_wait_bit			(struct bfe_softc *, u_int32_t, u_int32_t, 
 		u_long, const int);
 static void bfe_get_config			(struct bfe_softc *sc);
 static void bfe_read_eeprom			(struct bfe_softc *, u_int8_t *);
 static void bfe_stats_update		(struct bfe_softc *);
 static void bfe_clear_stats			(struct bfe_softc *);
 static int  bfe_readphy				(struct bfe_softc *, u_int32_t, u_int32_t*);
 static int  bfe_writephy			(struct bfe_softc *, u_int32_t, u_int32_t);
 static int  bfe_resetphy			(struct bfe_softc *);
 static int  bfe_setupphy			(struct bfe_softc *);
 static void bfe_chip_reset			(struct bfe_softc *);
 static void bfe_chip_halt			(struct bfe_softc *);
 static void bfe_core_reset			(struct bfe_softc *);
 static void bfe_core_disable		(struct bfe_softc *);
 static int  bfe_dma_alloc			(device_t);
 static void bfe_dma_map_desc		(void *, bus_dma_segment_t *, int, int);
 static void bfe_dma_map				(void *, bus_dma_segment_t *, int, int);
 static void bfe_cam_write			(struct bfe_softc *, u_char *, int);
 
 static device_method_t bfe_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		bfe_probe),
 	DEVMETHOD(device_attach,	bfe_attach),
 	DEVMETHOD(device_detach,	bfe_detach),
 	DEVMETHOD(device_shutdown,	bfe_shutdown),
 
 	/* bus interface */
 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
 	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
 
 	/* MII interface */
 	DEVMETHOD(miibus_readreg,	bfe_miibus_readreg),
 	DEVMETHOD(miibus_writereg,	bfe_miibus_writereg),
 	DEVMETHOD(miibus_statchg,	bfe_miibus_statchg),
 
 	{ 0, 0 }
 };
 
 static driver_t bfe_driver = {
 	"bfe",
 	bfe_methods,
 	sizeof(struct bfe_softc)
 };
 
 static devclass_t bfe_devclass;
 
 DRIVER_MODULE(bfe, pci, bfe_driver, bfe_devclass, 0, 0);
 DRIVER_MODULE(miibus, bfe, miibus_driver, miibus_devclass, 0, 0);
 
 /*
  * Probe for a Broadcom 4401 chip. 
  */
 static int
 bfe_probe(device_t dev)
 {
 	struct bfe_type *t;
 	struct bfe_softc *sc;
 
 	t = bfe_devs;
 
 	sc = device_get_softc(dev);
 	bzero(sc, sizeof(struct bfe_softc));
 	sc->bfe_unit = device_get_unit(dev);
 	sc->bfe_dev = dev;
 
 	while(t->bfe_name != NULL) {
 		if ((pci_get_vendor(dev) == t->bfe_vid) &&
 				(pci_get_device(dev) == t->bfe_did)) {
 			device_set_desc_copy(dev, t->bfe_name);
 			return(0);
 		}
 		t++;
 	}
 
 	return(ENXIO);
 }
 
 static int
 bfe_dma_alloc(device_t dev)
 {
 	struct bfe_softc *sc;
 	int error, i;
 
 	sc = device_get_softc(dev);
 
 	/* parent tag */
 	error = bus_dma_tag_create(NULL,  /* parent */
 			PAGE_SIZE, 0,             /* alignment, boundary */
 			BUS_SPACE_MAXADDR,        /* lowaddr */      
 			BUS_SPACE_MAXADDR_32BIT,  /* highaddr */
 			NULL, NULL,               /* filter, filterarg */
 			MAXBSIZE,                 /* maxsize */
 			BUS_SPACE_UNRESTRICTED,   /* num of segments */
 			BUS_SPACE_MAXSIZE_32BIT,  /* max segment size */
 			BUS_DMA_ALLOCNOW,         /* flags */
 			NULL, NULL,               /* lockfunc, lockarg */
 			&sc->bfe_parent_tag);
 
 	/* tag for TX ring */
 	error = bus_dma_tag_create(sc->bfe_parent_tag, BFE_TX_LIST_SIZE, 
 			BFE_TX_LIST_SIZE, BUS_SPACE_MAXADDR,  BUS_SPACE_MAXADDR, 
 			NULL, NULL, BFE_TX_LIST_SIZE, 1,  BUS_SPACE_MAXSIZE_32BIT,
 			0, NULL, NULL, &sc->bfe_tx_tag);
 
 	if (error) {
 		device_printf(dev, "could not allocate dma tag\n");
 		return(ENOMEM);
 	}
 
 	/* tag for RX ring */
 	error = bus_dma_tag_create(sc->bfe_parent_tag, BFE_RX_LIST_SIZE, 
 			BFE_RX_LIST_SIZE, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, 
 			NULL, NULL, BFE_RX_LIST_SIZE, 1, BUS_SPACE_MAXSIZE_32BIT, 
 			0, NULL, NULL, &sc->bfe_rx_tag);
 
 	if (error) {
 		device_printf(dev, "could not allocate dma tag\n");
 		return(ENOMEM);
 	}
 
 	/* tag for mbufs */
 	error = bus_dma_tag_create(sc->bfe_parent_tag, ETHER_ALIGN, 0,
 			BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 
 			1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->bfe_tag);
 
 	if (error) {
 		device_printf(dev, "could not allocate dma tag\n");
 		return(ENOMEM);
 	}
 
 	/* pre allocate dmamaps for RX list */
 	for (i = 0; i < BFE_RX_LIST_CNT; i++) {
 		error = bus_dmamap_create(sc->bfe_tag, 0, &sc->bfe_rx_ring[i].bfe_map);
 		if (error) {
 			device_printf(dev, "cannot create DMA map for RX\n");
 			return(ENOMEM);
 		}
 	}
 
 	/* pre allocate dmamaps for TX list */
 	for (i = 0; i < BFE_TX_LIST_CNT; i++) {
 		error = bus_dmamap_create(sc->bfe_tag, 0, &sc->bfe_tx_ring[i].bfe_map);
 		if (error) {
 			device_printf(dev, "cannot create DMA map for TX\n");
 			return(ENOMEM);
 		}
 	}
 
 	/* Alloc dma for rx ring */
 	error = bus_dmamem_alloc(sc->bfe_rx_tag, (void *)&sc->bfe_rx_list,
 			BUS_DMA_NOWAIT, &sc->bfe_rx_map);
 
 	if(error)
 		return(ENOMEM);
 
 	bzero(sc->bfe_rx_list, BFE_RX_LIST_SIZE);
 	error = bus_dmamap_load(sc->bfe_rx_tag, sc->bfe_rx_map,
 			sc->bfe_rx_list, sizeof(struct bfe_desc),
 			bfe_dma_map, &sc->bfe_rx_dma, 0);
 
 	if(error)
 		return(ENOMEM);
 
 	bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map, BUS_DMASYNC_PREREAD);
 
 	error = bus_dmamem_alloc(sc->bfe_tx_tag, (void *)&sc->bfe_tx_list, 
 			BUS_DMA_NOWAIT, &sc->bfe_tx_map);
 	if (error) 
 		return(ENOMEM);
 
 
 	error = bus_dmamap_load(sc->bfe_tx_tag, sc->bfe_tx_map, 
 			sc->bfe_tx_list, sizeof(struct bfe_desc), 
 			bfe_dma_map, &sc->bfe_tx_dma, 0);
 	if(error)
 		return(ENOMEM);
 
 	bzero(sc->bfe_tx_list, BFE_TX_LIST_SIZE);
 	bus_dmamap_sync(sc->bfe_tx_tag, sc->bfe_tx_map, BUS_DMASYNC_PREREAD);
 
 	return(0);
 }
 
 static int
 bfe_attach(device_t dev)
 {
 	struct ifnet *ifp;
 	struct bfe_softc *sc;
 	int unit, error = 0, rid;
 
 	sc = device_get_softc(dev);
 	mtx_init(&sc->bfe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
 			MTX_DEF | MTX_RECURSE);
 
 	unit = device_get_unit(dev);
 	sc->bfe_dev = dev;
 	sc->bfe_unit = unit;
 
 	/*
 	 * Handle power management nonsense.
 	 */
 	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
 		u_int32_t membase, irq;
 
 		/* Save important PCI config data. */
 		membase = pci_read_config(dev, BFE_PCI_MEMLO, 4);
 		irq = pci_read_config(dev, BFE_PCI_INTLINE, 4);
 
 		/* Reset the power state. */
 		printf("bfe%d: chip is is in D%d power mode -- setting to D0\n", 
 				sc->bfe_unit, pci_get_powerstate(dev));
 
 		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
 
 		/* Restore PCI config data. */
 		pci_write_config(dev, BFE_PCI_MEMLO, membase, 4);
 		pci_write_config(dev, BFE_PCI_INTLINE, irq, 4);
 	}
 
 	/*
 	 * Map control/status registers.
 	 */
 	pci_enable_busmaster(dev);
 
 	rid = BFE_PCI_MEMLO;
 	sc->bfe_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 
 			RF_ACTIVE);
 	if (sc->bfe_res == NULL) {
 		printf ("bfe%d: couldn't map memory\n", unit);
 		error = ENXIO;
 		goto fail;
 	}
 
 	sc->bfe_btag = rman_get_bustag(sc->bfe_res);
 	sc->bfe_bhandle = rman_get_bushandle(sc->bfe_res);
 	sc->bfe_vhandle = (vm_offset_t)rman_get_virtual(sc->bfe_res);
 
 	/* Allocate interrupt */
 	rid = 0;
 
 	sc->bfe_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
 			RF_SHAREABLE | RF_ACTIVE);
 	if (sc->bfe_irq == NULL) {
 		printf("bfe%d: couldn't map interrupt\n", unit);
 		error = ENXIO;
 		goto fail;
 	}
 
 	if (bfe_dma_alloc(dev)) {
 		printf("bfe%d: failed to allocate DMA resources\n", sc->bfe_unit);
 		bfe_release_resources(sc);
 		error = ENXIO;
 		goto fail;
 	}
 
 	/* Set up ifnet structure */
 	ifp = &sc->arpcom.ac_if;
 	ifp->if_softc = sc;
 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = bfe_ioctl;
 	ifp->if_output = ether_output;
 	ifp->if_start = bfe_start;
 	ifp->if_watchdog = bfe_watchdog;
 	ifp->if_init = bfe_init;
 	ifp->if_mtu = ETHERMTU;
 	ifp->if_baudrate = 10000000;
 	ifp->if_snd.ifq_maxlen = BFE_TX_QLEN;
 
 	bfe_get_config(sc);
 
 	printf("bfe%d: Ethernet address: %6D\n", unit, sc->arpcom.ac_enaddr, ":");
 
 	/* Reset the chip and turn on the PHY */
 	bfe_chip_reset(sc);
 
 	if (mii_phy_probe(dev, &sc->bfe_miibus,
 				bfe_ifmedia_upd, bfe_ifmedia_sts)) {
 		printf("bfe%d: MII without any PHY!\n", sc->bfe_unit);
 		error = ENXIO;
 		goto fail;
 	}
 
 	ether_ifattach(ifp, sc->arpcom.ac_enaddr);
 	callout_handle_init(&sc->bfe_stat_ch);
 
 	/*
 	 * Hook interrupt last to avoid having to lock softc
 	 */
 	error = bus_setup_intr(dev, sc->bfe_irq, INTR_TYPE_NET,
 			bfe_intr, sc, &sc->bfe_intrhand);
 
 	if (error) {
 		bfe_release_resources(sc);
 		printf("bfe%d: couldn't set up irq\n", unit);
 		goto fail;
 	}
 fail:
 	if(error)
 		bfe_release_resources(sc);
 	return(error);
 }
 
 static int
 bfe_detach(device_t dev)
 {
 	struct bfe_softc *sc;
 	struct ifnet *ifp;
 
 	sc = device_get_softc(dev);
 
 	KASSERT(mtx_initialized(&sc->bfe_mtx), ("bfe mutex not initialized"));
 	BFE_LOCK(scp);
 
 	ifp = &sc->arpcom.ac_if;
 
 	if (device_is_attached(dev)) {
 		bfe_stop(sc);
 		ether_ifdetach(ifp);
 	}
 
 	bfe_chip_reset(sc);
 
 	bus_generic_detach(dev);
 	if(sc->bfe_miibus != NULL)
 		device_delete_child(dev, sc->bfe_miibus);
 
 	bfe_release_resources(sc);
 	BFE_UNLOCK(sc);
 	mtx_destroy(&sc->bfe_mtx);
 
 	return(0);
 }
 
 /*
  * Stop all chip I/O so that the kernel's probe routines don't
  * get confused by errant DMAs when rebooting.
  */
 static void
 bfe_shutdown(device_t dev)
 {
 	struct bfe_softc *sc;
 
 	sc = device_get_softc(dev);
 	BFE_LOCK(sc);
 	bfe_stop(sc); 
 
 	BFE_UNLOCK(sc);
 	return;
 }
 
 static int
 bfe_miibus_readreg(device_t dev, int phy, int reg)
 {
 	struct bfe_softc *sc;
 	u_int32_t ret;
 
 	sc = device_get_softc(dev);
 	if(phy != sc->bfe_phyaddr)
 		return(0);
 	bfe_readphy(sc, reg, &ret);
 
 	return(ret);
 }
 
 static int
 bfe_miibus_writereg(device_t dev, int phy, int reg, int val)
 {
 	struct bfe_softc *sc;
 
 	sc = device_get_softc(dev);
 	if(phy != sc->bfe_phyaddr)
 		return(0);
 	bfe_writephy(sc, reg, val); 
 
 	return(0);
 }
 
 static void
 bfe_miibus_statchg(device_t dev)
 {
 	return;
 }
 
 static void
 bfe_tx_ring_free(struct bfe_softc *sc)
 {
     int i;
     
     for(i = 0; i < BFE_TX_LIST_CNT; i++) {
         if(sc->bfe_tx_ring[i].bfe_mbuf != NULL) {
             m_freem(sc->bfe_tx_ring[i].bfe_mbuf);
             sc->bfe_tx_ring[i].bfe_mbuf = NULL;
             bus_dmamap_unload(sc->bfe_tag,
                     sc->bfe_tx_ring[i].bfe_map);
             bus_dmamap_destroy(sc->bfe_tag,
                     sc->bfe_tx_ring[i].bfe_map);
         }
     }
     bzero(sc->bfe_tx_list, BFE_TX_LIST_SIZE);
     bus_dmamap_sync(sc->bfe_tx_tag, sc->bfe_tx_map, BUS_DMASYNC_PREREAD);
 }
 
 static void
 bfe_rx_ring_free(struct bfe_softc *sc)
 {
 	int i;
 
 	for (i = 0; i < BFE_RX_LIST_CNT; i++) {
 		if (sc->bfe_rx_ring[i].bfe_mbuf != NULL) {
 			m_freem(sc->bfe_rx_ring[i].bfe_mbuf);
 			sc->bfe_rx_ring[i].bfe_mbuf = NULL;
 			bus_dmamap_unload(sc->bfe_tag,
 					sc->bfe_rx_ring[i].bfe_map);
 			bus_dmamap_destroy(sc->bfe_tag,
 					sc->bfe_rx_ring[i].bfe_map);
 		}
 	}
 	bzero(sc->bfe_rx_list, BFE_RX_LIST_SIZE);
 	bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map, BUS_DMASYNC_PREREAD);
 }
 
 
 static int 
 bfe_list_rx_init(struct bfe_softc *sc)
 {
 	int i;
 
 	for(i = 0; i < BFE_RX_LIST_CNT; i++) {
 		if(bfe_list_newbuf(sc, i, NULL) == ENOBUFS) 
 			return ENOBUFS;
 	}
 
 	bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map, BUS_DMASYNC_PREREAD);
 	CSR_WRITE_4(sc, BFE_DMARX_PTR, (i * sizeof(struct bfe_desc)));
 
 	sc->bfe_rx_cons = 0;
 
 	return(0);
 }
 
 static int
 bfe_list_newbuf(struct bfe_softc *sc, int c, struct mbuf *m)
 {
 	struct bfe_rxheader *rx_header;
 	struct bfe_desc *d;
 	struct bfe_data *r;
 	u_int32_t ctrl;
 
 	if ((c < 0) || (c >= BFE_RX_LIST_CNT))
 		return(EINVAL);
 
 	if(m == NULL) {
 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
 		if(m == NULL)
 			return(ENOBUFS);
 		m->m_len = m->m_pkthdr.len = MCLBYTES;
 	}
 	else
 		m->m_data = m->m_ext.ext_buf;
 
 	rx_header = mtod(m, struct bfe_rxheader *);
 	rx_header->len = 0;
 	rx_header->flags = 0;
 
 	/* Map the mbuf into DMA */
 	sc->bfe_rx_cnt = c;
 	d = &sc->bfe_rx_list[c];
 	r = &sc->bfe_rx_ring[c];
 	bus_dmamap_load(sc->bfe_tag, r->bfe_map, mtod(m, void *), 
 			MCLBYTES, bfe_dma_map_desc, d, 0);
 	bus_dmamap_sync(sc->bfe_tag, r->bfe_map, BUS_DMASYNC_PREWRITE);
 
 	ctrl = ETHER_MAX_LEN + 32;
 
 	if(c == BFE_RX_LIST_CNT - 1)
 		ctrl |= BFE_DESC_EOT;
 
 	d->bfe_ctrl = ctrl;
 	r->bfe_mbuf = m;
 	bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map, BUS_DMASYNC_PREREAD);
 	return(0);
 }
 
 static void
 bfe_get_config(struct bfe_softc *sc)
 {
 	u_int8_t eeprom[128];
 
 	bfe_read_eeprom(sc, eeprom);
 
 	sc->arpcom.ac_enaddr[0] = eeprom[79];
 	sc->arpcom.ac_enaddr[1] = eeprom[78];
 	sc->arpcom.ac_enaddr[2] = eeprom[81];
 	sc->arpcom.ac_enaddr[3] = eeprom[80];
 	sc->arpcom.ac_enaddr[4] = eeprom[83];
 	sc->arpcom.ac_enaddr[5] = eeprom[82];
 
 	sc->bfe_phyaddr = eeprom[90] & 0x1f;
 	sc->bfe_mdc_port = (eeprom[90] >> 14) & 0x1;
 
 	sc->bfe_core_unit = 0; 
 	sc->bfe_dma_offset = BFE_PCI_DMA;
 }
 
 static void
 bfe_pci_setup(struct bfe_softc *sc, u_int32_t cores)
 {
 	u_int32_t bar_orig, pci_rev, val;
 
 	bar_orig = pci_read_config(sc->bfe_dev, BFE_BAR0_WIN, 4);
 	pci_write_config(sc->bfe_dev, BFE_BAR0_WIN, BFE_REG_PCI, 4);
 	pci_rev = CSR_READ_4(sc, BFE_SBIDHIGH) & BFE_RC_MASK;
 
 	val = CSR_READ_4(sc, BFE_SBINTVEC);
 	val |= cores;
 	CSR_WRITE_4(sc, BFE_SBINTVEC, val);
 
 	val = CSR_READ_4(sc, BFE_SSB_PCI_TRANS_2);
 	val |= BFE_SSB_PCI_PREF | BFE_SSB_PCI_BURST;
 	CSR_WRITE_4(sc, BFE_SSB_PCI_TRANS_2, val);
 
 	pci_write_config(sc->bfe_dev, BFE_BAR0_WIN, bar_orig, 4);
 }
 
 static void 
 bfe_clear_stats(struct bfe_softc *sc)
 {
 	u_long reg;
 
 	BFE_LOCK(sc);
 
 	CSR_WRITE_4(sc, BFE_MIB_CTRL, BFE_MIB_CLR_ON_READ);
 	for (reg = BFE_TX_GOOD_O; reg <= BFE_TX_PAUSE; reg += 4)
 		CSR_READ_4(sc, reg);
 	for (reg = BFE_RX_GOOD_O; reg <= BFE_RX_NPAUSE; reg += 4)
 		CSR_READ_4(sc, reg);
 
 	BFE_UNLOCK(sc);
 }
 
 static int 
 bfe_resetphy(struct bfe_softc *sc)
 {
 	u_int32_t val;
 
 	BFE_LOCK(sc);
 	bfe_writephy(sc, 0, BMCR_RESET);
 	DELAY(100);
 	bfe_readphy(sc, 0, &val);
 	if (val & BMCR_RESET) {
 		printf("bfe%d: PHY Reset would not complete.\n", sc->bfe_unit);
 		BFE_UNLOCK(sc);
 		return ENXIO;
 	}
 	BFE_UNLOCK(sc);
 	return 0;
 }
 
 static void
 bfe_chip_halt(struct bfe_softc *sc)
 {
 	BFE_LOCK(sc);
 	/* disable interrupts - not that it actually does..*/
 	CSR_WRITE_4(sc, BFE_IMASK, 0);
 	CSR_READ_4(sc, BFE_IMASK);
 
 	CSR_WRITE_4(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE);
 	bfe_wait_bit(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE, 200, 1);
 
 	CSR_WRITE_4(sc, BFE_DMARX_CTRL, 0);
 	CSR_WRITE_4(sc, BFE_DMATX_CTRL, 0);
 	DELAY(10);
 
 	BFE_UNLOCK(sc);
 }
 
 static void
 bfe_chip_reset(struct bfe_softc *sc)
 {
 	u_int32_t val;    
 
 	BFE_LOCK(sc);
 
 	/* Set the interrupt vector for the enet core */
 	bfe_pci_setup(sc, BFE_INTVEC_ENET0);
 
 	/* is core up? */
 	val = CSR_READ_4(sc, BFE_SBTMSLOW) & (BFE_RESET | BFE_REJECT | BFE_CLOCK);
 	if (val == BFE_CLOCK) {
 		/* It is, so shut it down */
 		CSR_WRITE_4(sc, BFE_RCV_LAZY, 0);
 		CSR_WRITE_4(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE);
 		bfe_wait_bit(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE, 100, 1);
 		CSR_WRITE_4(sc, BFE_DMATX_CTRL, 0);
 		sc->bfe_tx_cnt = sc->bfe_tx_prod = sc->bfe_tx_cons = 0;
 		if (CSR_READ_4(sc, BFE_DMARX_STAT) & BFE_STAT_EMASK) 
 			bfe_wait_bit(sc, BFE_DMARX_STAT, BFE_STAT_SIDLE, 100, 0);
 		CSR_WRITE_4(sc, BFE_DMARX_CTRL, 0);
 		sc->bfe_rx_prod = sc->bfe_rx_cons = 0;
 	}
 
 	bfe_core_reset(sc);
 	bfe_clear_stats(sc);
 
 	/*
 	 * We want the phy registers to be accessible even when
 	 * the driver is "downed" so initialize MDC preamble, frequency,
 	 * and whether internal or external phy here.
 	 */
 
 	/* 4402 has 62.5Mhz SB clock and internal phy */
 	CSR_WRITE_4(sc, BFE_MDIO_CTRL, 0x8d);
 
 	/* Internal or external PHY? */
 	val = CSR_READ_4(sc, BFE_DEVCTRL);
 	if(!(val & BFE_IPP)) 
 		CSR_WRITE_4(sc, BFE_ENET_CTRL, BFE_ENET_EPSEL);
 	else if(CSR_READ_4(sc, BFE_DEVCTRL) & BFE_EPR) {
 		BFE_AND(sc, BFE_DEVCTRL, ~BFE_EPR);
 		DELAY(100);
 	}
 
 	//BFE_OR(sc, BFE_MAC_CTRL, BFE_CTRL_CRC32_ENAB);
 	BFE_OR(sc, BFE_MAC_CTRL, BFE_CTRL_CRC32_ENAB | 7 << 5);
 	CSR_WRITE_4(sc, BFE_RCV_LAZY, ((1 << BFE_LAZY_FC_SHIFT) & 
 				BFE_LAZY_FC_MASK));
 
 	/* 
 	 * We don't want lazy interrupts, so just send them at the end of a frame,
 	 * please 
 	 */
 	BFE_OR(sc, BFE_RCV_LAZY, 0);
 
 	/* Set max lengths, accounting for VLAN tags */
 	CSR_WRITE_4(sc, BFE_RXMAXLEN, ETHER_MAX_LEN+32);
 	CSR_WRITE_4(sc, BFE_TXMAXLEN, ETHER_MAX_LEN+32);
 
 	/* Set watermark XXX - magic */
 	CSR_WRITE_4(sc, BFE_TX_WMARK, 56);
 
 	/* 
 	 * Initialise DMA channels - not forgetting dma addresses need to be added
 	 * to BFE_PCI_DMA 
 	 */
 	CSR_WRITE_4(sc, BFE_DMATX_CTRL, BFE_TX_CTRL_ENABLE);
 	CSR_WRITE_4(sc, BFE_DMATX_ADDR, sc->bfe_tx_dma + BFE_PCI_DMA);
 
 	CSR_WRITE_4(sc, BFE_DMARX_CTRL, (BFE_RX_OFFSET << BFE_RX_CTRL_ROSHIFT) | 
 			BFE_RX_CTRL_ENABLE);
 	CSR_WRITE_4(sc, BFE_DMARX_ADDR, sc->bfe_rx_dma + BFE_PCI_DMA);
 
 	bfe_resetphy(sc);
 	bfe_setupphy(sc);
 
 	BFE_UNLOCK(sc);
 }
 
 static void
 bfe_core_disable(struct bfe_softc *sc)
 {
 	if((CSR_READ_4(sc, BFE_SBTMSLOW)) & BFE_RESET)
 		return;
 
 	/* 
 	 * Set reject, wait for it set, then wait for the core to stop being busy
 	 * Then set reset and reject and enable the clocks
 	 */
 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_REJECT | BFE_CLOCK));
 	bfe_wait_bit(sc, BFE_SBTMSLOW, BFE_REJECT, 1000, 0);
 	bfe_wait_bit(sc, BFE_SBTMSHIGH, BFE_BUSY, 1000, 1);
 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_FGC | BFE_CLOCK | BFE_REJECT |
 				BFE_RESET));
 	CSR_READ_4(sc, BFE_SBTMSLOW);
 	DELAY(10);
 	/* Leave reset and reject set */
 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_REJECT | BFE_RESET));
 	DELAY(10);
 }
 
 static void
 bfe_core_reset(struct bfe_softc *sc)
 {
 	u_int32_t val;
 
 	/* Disable the core */
 	bfe_core_disable(sc);
 
 	/* and bring it back up */
 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_RESET | BFE_CLOCK | BFE_FGC));
 	CSR_READ_4(sc, BFE_SBTMSLOW);
 	DELAY(10);
 
 	/* Chip bug, clear SERR, IB and TO if they are set. */
 	if (CSR_READ_4(sc, BFE_SBTMSHIGH) & BFE_SERR)
 		CSR_WRITE_4(sc, BFE_SBTMSHIGH, 0);
 	val = CSR_READ_4(sc, BFE_SBIMSTATE);
 	if (val & (BFE_IBE | BFE_TO))
 		CSR_WRITE_4(sc, BFE_SBIMSTATE, val & ~(BFE_IBE | BFE_TO));
 
 	/* Clear reset and allow it to move through the core */
 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_CLOCK | BFE_FGC));
 	CSR_READ_4(sc, BFE_SBTMSLOW);
 	DELAY(10);
 
 	/* Leave the clock set */
 	CSR_WRITE_4(sc, BFE_SBTMSLOW, BFE_CLOCK);
 	CSR_READ_4(sc, BFE_SBTMSLOW);
 	DELAY(10);
 }
 
 static void 
 bfe_cam_write(struct bfe_softc *sc, u_char *data, int index)
 {
 	u_int32_t val;
 
 	val  = ((u_int32_t) data[2]) << 24;
 	val |= ((u_int32_t) data[3]) << 16;
 	val |= ((u_int32_t) data[4]) <<  8;
 	val |= ((u_int32_t) data[5]);
 	CSR_WRITE_4(sc, BFE_CAM_DATA_LO, val);
 	val = (BFE_CAM_HI_VALID |
 			(((u_int32_t) data[0]) << 8) |
 			(((u_int32_t) data[1])));
 	CSR_WRITE_4(sc, BFE_CAM_DATA_HI, val);
 	CSR_WRITE_4(sc, BFE_CAM_CTRL, (BFE_CAM_WRITE |
 				(index << BFE_CAM_INDEX_SHIFT)));
 	bfe_wait_bit(sc, BFE_CAM_CTRL, BFE_CAM_BUSY, 10000, 1);
 }
 
 static void 
 bfe_set_rx_mode(struct bfe_softc *sc)
 {
 	struct ifnet *ifp = &sc->arpcom.ac_if;
 	struct ifmultiaddr  *ifma;
 	u_int32_t val;
 	int i = 0;
 
 	val = CSR_READ_4(sc, BFE_RXCONF);
 
 	if (ifp->if_flags & IFF_PROMISC)
 		val |= BFE_RXCONF_PROMISC;
 	else
 		val &= ~BFE_RXCONF_PROMISC;
 
 	if (ifp->if_flags & IFF_BROADCAST)
 		val &= ~BFE_RXCONF_DBCAST;
 	else
 		val |= BFE_RXCONF_DBCAST;
 
 
 	CSR_WRITE_4(sc, BFE_CAM_CTRL, 0);
 	bfe_cam_write(sc, sc->arpcom.ac_enaddr, i++);
 
 	if (ifp->if_flags & IFF_ALLMULTI)
 		val |= BFE_RXCONF_ALLMULTI;
 	else {
 		val &= ~BFE_RXCONF_ALLMULTI;
 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
 			if (ifma->ifma_addr->sa_family != AF_LINK)
 				continue;
 			bfe_cam_write(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
 					i++);
 		}
 	}
 
 	CSR_WRITE_4(sc, BFE_RXCONF, val);
 	BFE_OR(sc, BFE_CAM_CTRL, BFE_CAM_ENABLE);
 }
 
 static void
 bfe_dma_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
 {
 	u_int32_t *ptr;
 
 	ptr = arg;
 	*ptr = segs->ds_addr;
 }
 
 static void
 bfe_dma_map_desc(void *arg, bus_dma_segment_t *segs, int nseg, int error)
 {
 	struct bfe_desc *d;
 
 	d = arg;
 	/* The chip needs all addresses to be added to BFE_PCI_DMA */
 	d->bfe_addr = segs->ds_addr + BFE_PCI_DMA;
 }
 
 static void
 bfe_release_resources(struct bfe_softc *sc)
 {
 	device_t dev;
 	int i;
 
 	dev = sc->bfe_dev;
 
 	if (sc->bfe_vpd_prodname != NULL)
 		free(sc->bfe_vpd_prodname, M_DEVBUF);
 
 	if (sc->bfe_vpd_readonly != NULL)
 		free(sc->bfe_vpd_readonly, M_DEVBUF);
 
 	if (sc->bfe_intrhand != NULL)
 		bus_teardown_intr(dev, sc->bfe_irq, sc->bfe_intrhand);
 
 	if (sc->bfe_irq != NULL)
 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->bfe_irq);
 
 	if (sc->bfe_res != NULL)
 		bus_release_resource(dev, SYS_RES_MEMORY, 0x10, sc->bfe_res);
 
 	if(sc->bfe_tx_tag != NULL) {
 		bus_dmamap_unload(sc->bfe_tx_tag, sc->bfe_tx_map);
 		bus_dmamem_free(sc->bfe_tx_tag, sc->bfe_tx_list, sc->bfe_tx_map);
 		bus_dma_tag_destroy(sc->bfe_tx_tag);
 		sc->bfe_tx_tag = NULL;
 	}
 
 	if(sc->bfe_rx_tag != NULL) {
 		bus_dmamap_unload(sc->bfe_rx_tag, sc->bfe_rx_map);
 		bus_dmamem_free(sc->bfe_rx_tag, sc->bfe_rx_list, sc->bfe_rx_map);
 		bus_dma_tag_destroy(sc->bfe_rx_tag);
 		sc->bfe_rx_tag = NULL;
 	}
 
 	if(sc->bfe_tag != NULL) {
 		for(i = 0; i < BFE_TX_LIST_CNT; i++) {
 			bus_dmamap_destroy(sc->bfe_tag, sc->bfe_tx_ring[i].bfe_map);
 		}
 		bus_dma_tag_destroy(sc->bfe_tag);
         sc->bfe_tag = NULL;
 	}
 
 	if(sc->bfe_parent_tag != NULL)
 		bus_dma_tag_destroy(sc->bfe_parent_tag);
 
 	return;
 }
 
 static void
 bfe_read_eeprom(struct bfe_softc *sc, u_int8_t *data)
 {
 	long i;
 	u_int16_t *ptr = (u_int16_t *)data;
 
 	for(i = 0; i < 128; i += 2)
 		ptr[i/2] = CSR_READ_4(sc, 4096 + i);
 }
 
 static int
 bfe_wait_bit(struct bfe_softc *sc, u_int32_t reg, u_int32_t bit, 
 		u_long timeout, const int clear)
 {
 	u_long i;
 
 	for (i = 0; i < timeout; i++) {
 		u_int32_t val = CSR_READ_4(sc, reg);
 
 		if (clear && !(val & bit))
 			break;
 		if (!clear && (val & bit))
 			break;
 		DELAY(10);
 	}
 	if (i == timeout) {
 		printf("bfe%d: BUG!  Timeout waiting for bit %08x of register "
 				"%x to %s.\n", sc->bfe_unit, bit, reg, 
 				(clear ? "clear" : "set"));
 		return -1;
 	}
 	return 0;
 }
 
 static int
 bfe_readphy(struct bfe_softc *sc, u_int32_t reg, u_int32_t *val)
 {
 	int err; 
 
 	BFE_LOCK(sc);
 	/* Clear MII ISR */
 	CSR_WRITE_4(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII);
 	CSR_WRITE_4(sc, BFE_MDIO_DATA, (BFE_MDIO_SB_START |
 				(BFE_MDIO_OP_READ << BFE_MDIO_OP_SHIFT) |
 				(sc->bfe_phyaddr << BFE_MDIO_PMD_SHIFT) |
 				(reg << BFE_MDIO_RA_SHIFT) |
 				(BFE_MDIO_TA_VALID << BFE_MDIO_TA_SHIFT)));
 	err = bfe_wait_bit(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII, 100, 0);
 	*val = CSR_READ_4(sc, BFE_MDIO_DATA) & BFE_MDIO_DATA_DATA;
 
 	BFE_UNLOCK(sc);
 	return err;
 }
 
 static int
 bfe_writephy(struct bfe_softc *sc, u_int32_t reg, u_int32_t val)
 {
 	int status;
 
 	BFE_LOCK(sc);
 	CSR_WRITE_4(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII);
 	CSR_WRITE_4(sc, BFE_MDIO_DATA, (BFE_MDIO_SB_START |
 				(BFE_MDIO_OP_WRITE << BFE_MDIO_OP_SHIFT) |
 				(sc->bfe_phyaddr << BFE_MDIO_PMD_SHIFT) |
 				(reg << BFE_MDIO_RA_SHIFT) |
 				(BFE_MDIO_TA_VALID << BFE_MDIO_TA_SHIFT) |
 				(val & BFE_MDIO_DATA_DATA)));
 	status = bfe_wait_bit(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII, 100, 0);
 	BFE_UNLOCK(sc);
 
 	return status;
 }
 
 /* 
  * XXX - I think this is handled by the PHY driver, but it can't hurt to do it
  * twice
  */
 static int
 bfe_setupphy(struct bfe_softc *sc)
 {
 	u_int32_t val;
 	BFE_LOCK(sc);
 
 	/* Enable activity LED */
 	bfe_readphy(sc, 26, &val);
 	bfe_writephy(sc, 26, val & 0x7fff); 
 	bfe_readphy(sc, 26, &val);
 
 	/* Enable traffic meter LED mode */
 	bfe_readphy(sc, 27, &val);
 	bfe_writephy(sc, 27, val | (1 << 6));
 
 	BFE_UNLOCK(sc);
 	return 0;
 }
 
 static void 
 bfe_stats_update(struct bfe_softc *sc)
 {
 	u_long reg;
 	u_int32_t *val;
 
 	val = &sc->bfe_hwstats.tx_good_octets;
 	for (reg = BFE_TX_GOOD_O; reg <= BFE_TX_PAUSE; reg += 4) {
 		*val++ += CSR_READ_4(sc, reg);
 	}
 	val = &sc->bfe_hwstats.rx_good_octets;
 	for (reg = BFE_RX_GOOD_O; reg <= BFE_RX_NPAUSE; reg += 4) {
 		*val++ += CSR_READ_4(sc, reg);
 	}
 }
 
 static void
 bfe_txeof(struct bfe_softc *sc)
 {
 	struct ifnet *ifp;
 	int i, chipidx;
 
 	BFE_LOCK(sc);
 
 	ifp = &sc->arpcom.ac_if;
 
 	chipidx = CSR_READ_4(sc, BFE_DMATX_STAT) & BFE_STAT_CDMASK;
 	chipidx /= sizeof(struct bfe_desc);
 
     i = sc->bfe_tx_cons;
 	/* Go through the mbufs and free those that have been transmitted */
     while(i != chipidx) {
 		struct bfe_data *r = &sc->bfe_tx_ring[i];
 		if(r->bfe_mbuf != NULL) {
 			ifp->if_opackets++;
 			m_freem(r->bfe_mbuf);
 			r->bfe_mbuf = NULL;
 			bus_dmamap_unload(sc->bfe_tag, r->bfe_map);
 		}
         sc->bfe_tx_cnt--;
         BFE_INC(i, BFE_TX_LIST_CNT);
 	}
 
 	if(i != sc->bfe_tx_cons) {
 		/* we freed up some mbufs */
 		sc->bfe_tx_cons = i;
 		ifp->if_flags &= ~IFF_OACTIVE;
 	}
 	if(sc->bfe_tx_cnt == 0)
 		ifp->if_timer = 0;
 	else
 		ifp->if_timer = 5;
 
 	BFE_UNLOCK(sc);
 }
 
 /* Pass a received packet up the stack */
 static void
 bfe_rxeof(struct bfe_softc *sc)
 {
 	struct mbuf *m;
 	struct ifnet *ifp;
 	struct bfe_rxheader *rxheader;
 	struct bfe_data *r;
 	int cons;
 	u_int32_t status, current, len, flags;
 
 	BFE_LOCK(sc);
 	cons = sc->bfe_rx_cons;
 	status = CSR_READ_4(sc, BFE_DMARX_STAT);
 	current = (status & BFE_STAT_CDMASK) / sizeof(struct bfe_desc);
 
 	ifp = &sc->arpcom.ac_if;
 
 	while(current != cons) {
 		r = &sc->bfe_rx_ring[cons];
 		m = r->bfe_mbuf;
 		rxheader = mtod(m, struct bfe_rxheader*);
 		bus_dmamap_sync(sc->bfe_tag, r->bfe_map, BUS_DMASYNC_POSTWRITE);
 		len = rxheader->len;
 		r->bfe_mbuf = NULL;
 
 		bus_dmamap_unload(sc->bfe_tag, r->bfe_map);
 		flags = rxheader->flags;
 
 		len -= ETHER_CRC_LEN;
 
 		/* flag an error and try again */
 		if ((len > ETHER_MAX_LEN+32) || (flags & BFE_RX_FLAG_ERRORS)) {
 			ifp->if_ierrors++;
 			if (flags & BFE_RX_FLAG_SERR)
 				ifp->if_collisions++;
 			bfe_list_newbuf(sc, cons, m);
 			continue;
 		}
 
 		/* Go past the rx header */
 		if (bfe_list_newbuf(sc, cons, NULL) == 0) {
 			m_adj(m, BFE_RX_OFFSET);
 			m->m_len = m->m_pkthdr.len = len;
 		} else {
 			bfe_list_newbuf(sc, cons, m);
 			ifp->if_ierrors++;
 			continue;
 		}
 
 		ifp->if_ipackets++;
 		m->m_pkthdr.rcvif = ifp;
 		BFE_UNLOCK(sc);
 		(*ifp->if_input)(ifp, m);
 		BFE_LOCK(sc);
 
         BFE_INC(cons, BFE_RX_LIST_CNT);
 	}
 	sc->bfe_rx_cons = cons;
 	BFE_UNLOCK(sc);
 }
 
 static void
 bfe_intr(void *xsc)
 {
 	struct bfe_softc *sc = xsc;
 	struct ifnet *ifp;
 	u_int32_t istat, imask, flag;
 
 	ifp = &sc->arpcom.ac_if;
 
 	BFE_LOCK(sc);
 
 	istat = CSR_READ_4(sc, BFE_ISTAT);
 	imask = CSR_READ_4(sc, BFE_IMASK);
 
 	/* 
 	 * Defer unsolicited interrupts - This is necessary because setting the
 	 * chips interrupt mask register to 0 doesn't actually stop the
 	 * interrupts
 	 */
 	istat &= imask;
 	CSR_WRITE_4(sc, BFE_ISTAT, istat);
 	CSR_READ_4(sc, BFE_ISTAT);
 
 	/* not expecting this interrupt, disregard it */
 	if(istat == 0) {
 		BFE_UNLOCK(sc);
 		return;
 	}
 
 	if(istat & BFE_ISTAT_ERRORS) {
 		flag = CSR_READ_4(sc, BFE_DMATX_STAT);
 		if(flag & BFE_STAT_EMASK)
 			ifp->if_oerrors++;
 
 		flag = CSR_READ_4(sc, BFE_DMARX_STAT);
 		if(flag & BFE_RX_FLAG_ERRORS)
 			ifp->if_ierrors++;
 
 		ifp->if_flags &= ~IFF_RUNNING;
 		bfe_init(sc);
 	}
 
 	/* A packet was received */
 	if(istat & BFE_ISTAT_RX)
 		bfe_rxeof(sc);
 
 	/* A packet was sent */
 	if(istat & BFE_ISTAT_TX)
 		bfe_txeof(sc);
 
 	/* We have packets pending, fire them out */ 
 	if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL)
 		bfe_start(ifp);
 
 	BFE_UNLOCK(sc);
 }
 
 static int
 bfe_encap(struct bfe_softc *sc, struct mbuf *m_head, u_int32_t *txidx)
 {
 	struct bfe_desc *d = NULL;
 	struct bfe_data *r = NULL;
 	struct mbuf     *m;
 	u_int32_t       frag, cur, cnt = 0;
 	int chainlen = 0;
 
 	if(BFE_TX_LIST_CNT - sc->bfe_tx_cnt < 2)
 		return(ENOBUFS);
 
 	/*
 	 * Count the number of frags in this chain to see if
 	 * we need to m_defrag.  Since the descriptor list is shared
 	 * by all packets, we'll m_defrag long chains so that they
 	 * do not use up the entire list, even if they would fit.
 	 */
 	for(m = m_head; m != NULL; m = m->m_next) 
 		chainlen++;
 
 
 	if ((chainlen > BFE_TX_LIST_CNT / 4) || 
 			((BFE_TX_LIST_CNT - (chainlen + sc->bfe_tx_cnt)) < 2)) {
 		m = m_defrag(m_head, M_DONTWAIT);
 		if (m == NULL) 
 			return(ENOBUFS);
 		m_head = m;
 	}
 
 	/*
 	 * Start packing the mbufs in this chain into
 	 * the fragment pointers. Stop when we run out
 	 * of fragments or hit the end of the mbuf chain.
 	 */
 	m = m_head;
 	cur = frag = *txidx;
 	cnt = 0;
 
 	for(m = m_head; m != NULL; m = m->m_next) {
 		if(m->m_len != 0) {
 			if((BFE_TX_LIST_CNT - (sc->bfe_tx_cnt + cnt)) < 2)
 				return(ENOBUFS);
 
 			d = &sc->bfe_tx_list[cur];
 			r = &sc->bfe_tx_ring[cur];
 			d->bfe_ctrl = BFE_DESC_LEN & m->m_len;
 			/* always intterupt on completion */
 			d->bfe_ctrl |= BFE_DESC_IOC;
 			if(cnt == 0)
 				/* Set start of frame */
 				d->bfe_ctrl |= BFE_DESC_SOF;
 			if(cur == BFE_TX_LIST_CNT - 1)
 				/* Tell the chip to wrap to the start of the descriptor list */
 				d->bfe_ctrl |= BFE_DESC_EOT;
 
 			bus_dmamap_load(sc->bfe_tag, r->bfe_map, mtod(m, void*), m->m_len, 
 					bfe_dma_map_desc, d, 0);
 			bus_dmamap_sync(sc->bfe_tag, r->bfe_map, BUS_DMASYNC_PREREAD);
 
 			frag = cur;
             BFE_INC(cur, BFE_TX_LIST_CNT);
 			cnt++;
 		}
 	}
 
 	if (m != NULL)
 		return(ENOBUFS);
 
 	sc->bfe_tx_list[frag].bfe_ctrl |= BFE_DESC_EOF;
 	sc->bfe_tx_ring[frag].bfe_mbuf = m_head;
 	bus_dmamap_sync(sc->bfe_tx_tag, sc->bfe_tx_map, BUS_DMASYNC_PREREAD);
 
 	*txidx = cur;
 	sc->bfe_tx_cnt += cnt;
 	return (0);
 }
 
 /*
  * Set up to transmit a packet
  */
 static void
 bfe_start(struct ifnet *ifp)
 {
 	struct bfe_softc *sc;
 	struct mbuf *m_head = NULL;
 	int idx;
 
 	sc = ifp->if_softc;
 	idx = sc->bfe_tx_prod;
 
 	BFE_LOCK(sc);
 
 	/* 
 	 * not much point trying to send if the link is down or we have nothing to
 	 * send
 	 */
 	if (!sc->bfe_link && ifp->if_snd.ifq_len < 10) {
 		BFE_UNLOCK(sc);
 		return;
 	}
 
 	if (ifp->if_flags & IFF_OACTIVE) {
 		BFE_UNLOCK(sc);
 		return;
 	}
 
 	while(sc->bfe_tx_ring[idx].bfe_mbuf == NULL) {
 		IF_DEQUEUE(&ifp->if_snd, m_head);
 		if(m_head == NULL)
 			break;
 
 		/* 
 		 * Pack the data into the tx ring.  If we dont have enough room, let
 		 * the chip drain the ring
 		 */
 		if(bfe_encap(sc, m_head, &idx)) {
 			IF_PREPEND(&ifp->if_snd, m_head);
 			ifp->if_flags |= IFF_OACTIVE;
 			break;
 		}
 
 		/*
 		 * If there's a BPF listener, bounce a copy of this frame
 		 * to him.
 		 */
 		BPF_MTAP(ifp, m_head);
 	}
 
 	sc->bfe_tx_prod = idx;
 	/* Transmit - twice due to apparent hardware bug */
 	CSR_WRITE_4(sc, BFE_DMATX_PTR, idx * sizeof(struct bfe_desc));
 	CSR_WRITE_4(sc, BFE_DMATX_PTR, idx * sizeof(struct bfe_desc));
 
 	/*
 	 * Set a timeout in case the chip goes out to lunch.
 	 */
 	ifp->if_timer = 5;
 	BFE_UNLOCK(sc);
 }
 
 static void
 bfe_init(void *xsc)
 {
 
     uint32_t val1, val2, val3;
 
 	struct bfe_softc *sc = (struct bfe_softc*)xsc;
 	struct ifnet *ifp = &sc->arpcom.ac_if;
 
 	BFE_LOCK(sc);
 
 	if (ifp->if_flags & IFF_RUNNING) {
 		BFE_UNLOCK(sc);
 		return;
 	}
 
 /* for test only */ 
 	val1 = CSR_READ_4(sc, BFE_CAM_CTRL);
     val2 = CSR_READ_4(sc, BFE_MAC_CTRL); 
 	val3 = CSR_READ_4(sc, BFE_ENET_CTRL);
 	printf("At entry: CAM_CTRL =%08x, MAC_CTRL =%08x, ENET_CTRL=%08x\n",
             val1, val2, val3);
 /* for test end */
 
 	bfe_stop(sc);
 	bfe_chip_reset(sc);
 
 /* for test only */ 
 	val1 = CSR_READ_4(sc, BFE_CAM_CTRL);
     val2 = CSR_READ_4(sc, BFE_MAC_CTRL); 
 	val3 = CSR_READ_4(sc, BFE_ENET_CTRL);
 	printf("After reset: CAM_CTRL =%08x, MAC_CTRL =%08x, ENET_CTRL=%08x\n",
             val1, val2, val3);
 /* for test end */
 
 	if (bfe_list_rx_init(sc) == ENOBUFS) {
 		printf("bfe%d: bfe_init failed. Not enough memory for list buffers\n",
 				sc->bfe_unit);
 		bfe_stop(sc);
 		return;
 	}
 
 	bfe_set_rx_mode(sc);
 
 	/* Enable the chip and core */
 	BFE_OR(sc, BFE_ENET_CTRL, BFE_ENET_ENABLE);
 	/* Enable interrupts */
 	CSR_WRITE_4(sc, BFE_IMASK, BFE_IMASK_DEF);
 
 	bfe_ifmedia_upd(ifp);
 	ifp->if_flags |= IFF_RUNNING;
 	ifp->if_flags &= ~IFF_OACTIVE;
 
 	sc->bfe_stat_ch = timeout(bfe_tick, sc, hz);
 	BFE_UNLOCK(sc);
 
 /* for test only */ 
 	val1 = CSR_READ_4(sc, BFE_CAM_CTRL);
     val2 = CSR_READ_4(sc, BFE_MAC_CTRL); 
 	val3 = CSR_READ_4(sc, BFE_ENET_CTRL);
 	printf("At exit: CAM_CTRL =%08x, MAC_CTRL =%08x, ENET_CTRL=%08x\n",
             val1, val2, val3);
 /* for test end */
 
 }
 
 /*
  * Set media options.
  */
 static int
 bfe_ifmedia_upd(struct ifnet *ifp)
 {
 	struct bfe_softc *sc;
 	struct mii_data *mii;
 
 	sc = ifp->if_softc;
 
 	BFE_LOCK(sc);
 
 	mii = device_get_softc(sc->bfe_miibus);
 	sc->bfe_link = 0;
 	if (mii->mii_instance) {
 		struct mii_softc *miisc;
 		for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
 				miisc = LIST_NEXT(miisc, mii_list))
 			mii_phy_reset(miisc);
 	}
 	mii_mediachg(mii);
 
 	BFE_UNLOCK(sc);
 	return(0);
 }
 
 /*
  * Report current media status.
  */
 static void
 bfe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
 {
 	struct bfe_softc *sc = ifp->if_softc;
 	struct mii_data *mii;
 
 	BFE_LOCK(sc);
 
 	mii = device_get_softc(sc->bfe_miibus);
 	mii_pollstat(mii);
 	ifmr->ifm_active = mii->mii_media_active;
 	ifmr->ifm_status = mii->mii_media_status;
 
 	BFE_UNLOCK(sc);
 }
 
 static int
 bfe_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
 {
 	struct bfe_softc *sc = ifp->if_softc;
 	struct ifreq *ifr = (struct ifreq *) data;
 	struct mii_data *mii;
 	int error = 0;
 
 	BFE_LOCK(sc);
 
 	switch(command) {
 		case SIOCSIFFLAGS:
 			if(ifp->if_flags & IFF_UP)
 				if(ifp->if_flags & IFF_RUNNING)
 					bfe_set_rx_mode(sc);
 				else
 					bfe_init(sc);
 			else if(ifp->if_flags & IFF_RUNNING)
 				bfe_stop(sc);
 			break;
 		case SIOCADDMULTI:
 		case SIOCDELMULTI:
 			if(ifp->if_flags & IFF_RUNNING)
 				bfe_set_rx_mode(sc);
 			break;
 		case SIOCGIFMEDIA:
 		case SIOCSIFMEDIA:
 			mii = device_get_softc(sc->bfe_miibus);
 			error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
 			break;
 		default:
 			error = ether_ioctl(ifp, command, data); 
 			break;
 	}
 
 	BFE_UNLOCK(sc);
 	return error;
 }
 
 static void
 bfe_watchdog(struct ifnet *ifp)
 {
 	struct bfe_softc *sc;
 
 	sc = ifp->if_softc;
 
 	BFE_LOCK(sc);
 
 	printf("bfe%d: watchdog timeout -- resetting\n", sc->bfe_unit);
 
 	ifp->if_flags &= ~IFF_RUNNING;
 	bfe_init(sc);
 
 	ifp->if_oerrors++;
 
 	BFE_UNLOCK(sc);
 }
 
 static void
 bfe_tick(void *xsc)
 {
 	struct bfe_softc *sc = xsc;
 	struct mii_data *mii;
 
 	if (sc == NULL)
 		return;
 
 	BFE_LOCK(sc);
 
 	mii = device_get_softc(sc->bfe_miibus);
 
 	bfe_stats_update(sc);
 	sc->bfe_stat_ch = timeout(bfe_tick, sc, hz);
 
 	if(sc->bfe_link) {
 		BFE_UNLOCK(sc);
 		return;
 	}
 
 	mii_tick(mii);
 	if (!sc->bfe_link && mii->mii_media_status & IFM_ACTIVE &&
 			IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) 
 		sc->bfe_link++;
 
 	BFE_UNLOCK(sc);
 }
 
 /*
  * Stop the adapter and free any mbufs allocated to the
  * RX and TX lists.
  */
 static void
 bfe_stop(struct bfe_softc *sc)
 {
 	struct ifnet *ifp;
 
 	BFE_LOCK(sc);
 
 	untimeout(bfe_tick, sc, sc->bfe_stat_ch);
 
 	ifp = &sc->arpcom.ac_if;
 
 	bfe_chip_halt(sc);
     bfe_tx_ring_free(sc);
 	bfe_rx_ring_free(sc);
 
 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
 
 	BFE_UNLOCK(sc);
 }
 
 --------------090505010100030000050608--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200405101400.i4AE0is9064715>