Index: bsps/powerpc/mvme3100/net/tsec.c
===================================================================
--- bsps/powerpc/mvme3100/net/tsec.c	(revision 031df3914990db0336a0d386fb53558b05de467e)
+++ bsps/powerpc/mvme3100/net/tsec.c	(revision 031df3914990db0336a0d386fb53558b05de467e)
@@ -0,0 +1,3215 @@
+/*
+ * Authorship
+ * ----------
+ * This software ('mvme3100' RTEMS BSP) was created by
+ *
+ *     Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
+ * 	   Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The 'mvme3100' BSP was produced by
+ *     the Stanford Linear Accelerator Center, Stanford University,
+ * 	   under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software.  Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <bsp/irq.h>
+#include <libcpu/byteorder.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <bsp.h>
+
+#ifndef KERNEL
+#define KERNEL
+#endif
+#ifndef _KERNEL
+#define _KERNEL
+#endif
+
+#include <rtems/rtems_bsdnet.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <rtems/rtems_mii_ioctl.h>
+
+#include <bsp/if_tsec_pub.h>
+
+#define STATIC
+#define PARANOIA
+#undef  DEBUG
+
+
+#ifdef TEST_MII_TIMING
+
+#include <libcpu/spr.h>
+
+SPR_RO(TBRL)
+
+static inline uint32_t tb_rd()
+{
+	return _read_TBRL();
+}
+#endif
+
+struct tsec_private;
+
+/* Forward declarations */
+static void
+phy_init_irq( int install, struct tsec_private *mp, void (*tsec_lisr)(rtems_irq_hdl_param) );
+
+static void
+phy_en_irq(struct tsec_private *mp);
+
+static void
+phy_en_irq_at_phy(struct tsec_private *mp);
+
+static void
+phy_dis_irq(struct tsec_private *mp);
+
+static void
+phy_dis_irq_at_phy(struct tsec_private *mp);
+
+static int
+phy_irq_pending(struct tsec_private *mp);
+
+static uint32_t
+phy_ack_irq(struct tsec_private *mp);
+
+static void
+tsec_update_mcast(struct ifnet *ifp);
+
+#if defined(PARANOIA) || defined(DEBUG)
+void tsec_dump_tring(struct tsec_private *mp);
+void tsec_dump_rring(struct tsec_private *mp);
+#endif
+
+#ifdef DEBUG
+#ifdef  TSEC_RX_RING_SIZE
+#undef  TSEC_RX_RING_SIZE
+#endif
+#define TSEC_RX_RING_SIZE		4
+
+#ifdef  TSEC_TX_RING_SIZE
+#undef  TSEC_TX_RING_SIZE
+#endif
+#define TSEC_TX_RING_SIZE		2
+#else
+#ifndef TSEC_RX_RING_SIZE
+#define TSEC_RX_RING_SIZE		40
+#endif
+#ifndef TSEC_TX_RING_SIZE
+#define TSEC_TX_RING_SIZE		200
+#endif
+#endif
+
+/********** Helper Macros and Definitions ******/
+
+/*
+ * Align 'p' up to a multiple of 'a' which must be
+ * a power of two. Result is cast to (uintptr_t)
+ */
+#define ALIGNTO(p,a)	((((uintptr_t)(p)) + (a) - 1) & ~((a)-1))
+
+
+/*
+ * Not obvious from the doc: RX buffers apparently must be 32-byte
+ * aligned :-(; TX buffers have no alignment requirement.
+ * I found this by testing, T.S, 11/2007
+ */
+#define RX_BUF_ALIGNMENT		32
+
+/*
+ * Alignment req. for buffer descriptors (BDs)
+ */
+#define BD_ALIGNMENT 8
+
+
+#define ETH_RX_OFFSET			0
+#define ETH_CRC_LEN				0
+
+#define CPU2BUS_ADDR(x)			(x)
+#define BUS2CPU_ADDR(x)			(x)
+
+/*
+ * Whether to automatically try to reclaim descriptors
+ * when enqueueing new packets
+ */
+#if 1
+#define TSEC_CLEAN_ON_SEND(mp) (BSP_tsec_swipe_tx(mp))
+#else
+#define TSEC_CLEAN_ON_SEND(mp) (-1)
+#endif
+
+#define TX_AVAILABLE_RING_SIZE(mp)	(mp)->tx_ring_size
+
+#define DRVNAME "tsec"
+
+/*
+ * Event(s) posted by ISRs to driver task
+ */
+#define EV_PER_UNIT				1
+
+#define TSEC_ETH_EVENT( unit )	(   1 << (EV_PER_UNIT * (unit)    ))
+#if EV_PER_UNIT > 1
+#define TSEC_PHY_EVENT( unit )  (   1 << (EV_PER_UNIT * (unit) + 1))
+#endif
+
+#define EV_IS_ETH(ev)			( (ev) & 1 )
+#if EV_PER_UNIT > 1
+#define EV_IS_PHY(ev)			( (ev) & 2 )
+#endif
+
+#ifndef MAXEBERRS
+#define MAXEBERRS 10
+#endif
+
+#define EV_IS_ANY(ev)			( (ev) & ((1<<EV_PER_UNIT) - 1) )
+
+#define EV_MSK                  ( ( 1 << (EV_PER_UNIT * TSEC_NUM_DRIVER_SLOTS) ) - 1)
+
+
+/********** Register Definitions ****************/
+
+/*
+ * Most registers/bits apply to the FEC also;
+ * things that are not supported by the FEC
+ * are commented 'TSEC only'
+ */
+
+#define TSEC_IEVENT							0x010
+#define TSEC_IEVENT_BABR						(1<<(31- 0))
+#define TSEC_IEVENT_RXC							(1<<(31- 1))
+#define TSEC_IEVENT_BSY							(1<<(31- 2))
+#define TSEC_IEVENT_EBERR						(1<<(31- 3))
+/*
+ * Misuse reserved bit 4 to flag link interrupts
+ * (which are totally external to the TSEC).
+ * Because reading the MII is so slow we don't
+ * want to poll MII unnecessarily from RX/TX ISRs
+ */
+#define TSEC_LINK_INTR							(1<<(31- 4))
+#define TSEC_IEVENT_MSRO						(1<<(31- 5))
+#define TSEC_IEVENT_GTSC						(1<<(31- 6))
+#define TSEC_IEVENT_BABT						(1<<(31- 7))
+#define TSEC_IEVENT_TXC							(1<<(31- 8))
+#define TSEC_IEVENT_TXE							(1<<(31- 9))
+#define TSEC_IEVENT_TXB							(1<<(31-10))
+#define TSEC_IEVENT_TXF							(1<<(31-11))
+#define TSEC_IEVENT_LC							(1<<(31-13))
+#define TSEC_IEVENT_CRLXDA						(1<<(31-14))
+#define TSEC_IEVENT_XFUN						(1<<(31-15))
+#define TSEC_IEVENT_RXB							(1<<(31-16))
+#define TSEC_IEVENT_GRSC						(1<<(31-23))
+#define TSEC_IEVENT_RXF							(1<<(31-24))
+#define TSEC_IEVENT_ALL							(-1)
+
+#if TSEC_TXIRQ != ( TSEC_IEVENT_TXE | TSEC_IEVENT_TXF )
+#error "mismatch in definition: TSEC_TXIRQ"
+#endif
+
+#if TSEC_RXIRQ != ( TSEC_IEVENT_RXF | TSEC_IEVENT_BABR | TSEC_IEVENT_EBERR )
+#error "mismatch in definition: TSEC_RXIRQ"
+#endif
+
+#if TSEC_LKIRQ != TSEC_LINK_INTR
+#error "mismatch in definition: TSEC_LKIRQ"
+#endif
+
+#define TSEC_IMASK							0x014
+#define TSEC_IMASK_BABREN						(1<<(31- 0))
+#define TSEC_IMASK_RXCEN						(1<<(31- 1))
+#define TSEC_IMASK_BSYEN						(1<<(31- 2))
+#define TSEC_IMASK_EBERREN						(1<<(31- 3))
+#define TSEC_IMASK_MSROEN						(1<<(31- 5))
+#define TSEC_IMASK_GTSCEN						(1<<(31- 6))
+#define TSEC_IMASK_BABTEN						(1<<(31- 7))
+#define TSEC_IMASK_TXCEN						(1<<(31- 8))
+#define TSEC_IMASK_TXEEN						(1<<(31- 9))
+#define TSEC_IMASK_TXBEN						(1<<(31-10))
+#define TSEC_IMASK_TXFEN						(1<<(31-11))
+#define TSEC_IMASK_LCEN							(1<<(31-13))
+#define TSEC_IMASK_CRLXDAEN						(1<<(31-14))
+#define TSEC_IMASK_XFUNEN						(1<<(31-15))
+#define TSEC_IMASK_RXBEN						(1<<(31-16))
+#define TSEC_IMASK_GRSCEN						(1<<(31-23))
+#define TSEC_IMASK_RXFEN						(1<<(31-24))
+#define TSEC_IMASK_NONE							(0)
+#define TSEC_EDIS							0x018
+#define TSEC_ECNTRL							0x020 /* TSEC only */
+#define TSEC_ECNTRL_CLRCNT						(1<<(31-17))
+#define TSEC_ECNTRL_AUTOZ						(1<<(31-18))
+#define TSEC_ECNTRL_STEN						(1<<(31-19))
+#define TSEC_ECNTRL_TBIM						(1<<(31-26))
+#define TSEC_ECNTRL_RPM							(1<<(31-27))
+#define TSEC_ECNTRL_R100M						(1<<(31-28))
+#define TSEC_MINFLR							0x024
+#define TSEC_PTV							0x028
+#define TSEC_DMACTRL						0x02c
+#define TSEC_DMACTRL_TDSEN						(1<<(31-24))
+#define TSEC_DMACTRL_TBDSEN						(1<<(31-25))
+#define TSEC_DMACTRL_GRS						(1<<(31-27))
+#define TSEC_DMACTRL_GTS						(1<<(31-28))
+#define TSEC_DMACTRL_WWR						(1<<(31-30))
+#define TSEC_DMACTRL_WOP						(1<<(31-31))
+#define TSEC_TBIPA							0x030 /* TSEC only */
+#define TSEC_FIFO_PAUSE_CTRL				0x04c
+#define TSEC_FIFO_TX_THR					0x08c
+#define TSEC_FIFO_TX_STARVE					0x098
+#define TSEC_FIFO_TX_STARVE_SHUTOFF			0x09c
+#define TSEC_TCTRL							0x100
+#define TSEC_TCTRL_THDR							(1<<(31-20))
+#define TSEC_TCTRL_RFC_PAUSE					(1<<(31-27))
+#define TSEC_TCTRL_TFC_PAUSE					(1<<(31-28))
+#define TSEC_TSTAT							0x104
+#define TSEC_TSTAT_THLT							(1<<(31- 0))
+#define TSEC_TBDLEN							0x10c
+#define TSEC_TXIC							0x110 /* TSEC only */
+#define TSEC_IC_ICEN							(1<<(31- 0))
+#define TSEC_IC_ICFCT(x)						(((x)&0xff)<<(31-10))
+#define TSEC_IC_ICTT(x)							(((x)&0xffff)<<(31-31))
+#define TSEC_CTBPTR							0x124
+#define TSEC_TBPTR							0x184
+#define TSEC_TBASE							0x204
+#define TSEC_OSTBD							0x2b0
+#define TSEC_OSTBDP							0x2b4
+#define TSEC_RCTRL							0x300
+#define TSEC_RCTRL_BC_REJ						(1<<(31-27))
+#define TSEC_RCTRL_PROM							(1<<(31-28))
+#define TSEC_RCTRL_RSF							(1<<(31-29))
+#define TSEC_RSTAT							0x304
+#define TSEC_RSTAT_QHLT							(1<<(31- 8))
+#define TSEC_RBDLEN							0x30c
+#define TSEC_RXIC							0x310 /* TSEC only */
+#define TSEC_CRBPTR							0x324
+#define TSEC_MRBLR							0x340
+#define TSEC_RBPTR							0x384
+#define TSEC_RBASE							0x404
+#define TSEC_MACCFG1						0x500
+#define TSEC_MACCFG1_SOFT_RESET					(1<<(31- 0))
+#define TSEC_MACCFG1_LOOP_BACK					(1<<(31-23))
+#define TSEC_MACCFG1_RX_FLOW					(1<<(31-26))
+#define TSEC_MACCFG1_TX_FLOW					(1<<(31-27))
+#define TSEC_MACCFG1_SYNCD_RX_EN				(1<<(31-28))
+#define TSEC_MACCFG1_RX_EN						(1<<(31-29))
+#define TSEC_MACCFG1_SYNCD_TX_EN				(1<<(31-30))
+#define TSEC_MACCFG1_TX_EN						(1<<(31-31))
+#define TSEC_MACCFG2						0x504
+#define TSEC_MACCFG2_PREAMBLE_7					(7<<(31-19))
+#define TSEC_MACCFG2_PREAMBLE_15 				(15<<(31-19))
+#define TSEC_MACCFG2_IF_MODE_MII				(1<<(31-23))
+#define TSEC_MACCFG2_IF_MODE_GMII				(2<<(31-23))
+#define TSEC_MACCFG2_HUGE_FRAME					(1<<(31-26))
+#define TSEC_MACCFG2_LENGTH_CHECK				(1<<(31-27))
+#define TSEC_MACCFG2_PAD_CRC					(1<<(31-29))
+#define TSEC_MACCFG2_CRC_EN						(1<<(31-30))
+#define TSEC_MACCFG2_FD							(1<<(31-31))
+#define TSEC_IPGIFG							0x508
+#define TSEC_HAFDUP							0x50c
+#define TSEC_MAXFRM							0x510
+#define TSEC_MIIMCFG						0x520 /* TSEC only */
+#define TSEC_MIIMCOM						0x524 /* TSEC only */
+#define TSEC_MIIMCOM_SCAN						(1<<(31-30))
+#define TSEC_MIIMCOM_READ						(1<<(31-31))
+#define TSEC_MIIMADD						0x528 /* TSEC only */
+#define TSEC_MIIMADD_ADDR(phy, reg)				((((phy)&0x1f)<<8) | ((reg) & 0x1f))
+#define TSEC_MIIMCON						0x52c /* TSEC only */
+#define TSEC_MIIMSTAT						0x530 /* TSEC only */
+#define TSEC_MIIMIND						0x534 /* TSEC only */
+#define TSEC_MIIMIND_NV							(1<<(31-29))
+#define TSEC_MIIMIND_SCAN						(1<<(31-30))
+#define TSEC_MIIMIND_BUSY						(1<<(31-31))
+#define TSEC_IFSTAT							0x53c
+#define TSEC_MACSTNADDR1					0x540
+#define TSEC_MACSTNADDR2					0x544
+#define TSEC_TR64							0x680 /* TSEC only */
+#define TSEC_TR127							0x684 /* TSEC only */
+#define TSEC_TR255							0x688 /* TSEC only */
+#define TSEC_TR511							0x68c /* TSEC only */
+#define TSEC_TR1K							0x690 /* TSEC only */
+#define TSEC_TRMAX							0x694 /* TSEC only */
+#define TSEC_TRMGV							0x698 /* TSEC only */
+#define TSEC_RBYT							0x69c /* TSEC only */
+#define TSEC_RPKT							0x6a0 /* TSEC only */
+#define TSEC_RFCS							0x6a4 /* TSEC only */
+#define TSEC_RMCA							0x6a8 /* TSEC only */
+#define TSEC_RBCA							0x6ac /* TSEC only */
+#define TSEC_RXCF							0x6b0 /* TSEC only */
+#define TSEC_RXPF							0x6b4 /* TSEC only */
+#define TSEC_RXUO							0x6b8 /* TSEC only */
+#define TSEC_RALN							0x6bc /* TSEC only */
+#define TSEC_RFLR							0x6c0 /* TSEC only */
+#define TSEC_RCDE							0x6c4 /* TSEC only */
+#define TSEC_RCSE							0x6c8 /* TSEC only */
+#define TSEC_RUND							0x6cc /* TSEC only */
+#define TSEC_ROVR							0x6d0 /* TSEC only */
+#define TSEC_RFRG							0x6d4 /* TSEC only */
+#define TSEC_RJBR							0x6d8 /* TSEC only */
+#define TSEC_RDRP							0x6dc /* TSEC only */
+#define TSEC_TBYT							0x6e0 /* TSEC only */
+#define TSEC_TPKT							0x6e4 /* TSEC only */
+#define TSEC_TMCA							0x6e8 /* TSEC only */
+#define TSEC_TBCA							0x6ec /* TSEC only */
+#define TSEC_TXPF							0x6f0 /* TSEC only */
+#define TSEC_TDFR							0x6f4 /* TSEC only */
+#define TSEC_TEDF							0x6f8 /* TSEC only */
+#define TSEC_TSCL							0x6fc /* TSEC only */
+#define TSEC_TMCL							0x700 /* TSEC only */
+#define TSEC_TLCL							0x704 /* TSEC only */
+#define TSEC_TXCL							0x708 /* TSEC only */
+#define TSEC_TNCL							0x70c /* TSEC only */
+#define TSEC_TDRP							0x714 /* TSEC only */
+#define TSEC_TJBR							0x718 /* TSEC only */
+#define TSEC_TFCS							0x71c /* TSEC only */
+#define TSEC_TXCF							0x720 /* TSEC only */
+#define TSEC_TOVR							0x724 /* TSEC only */
+#define TSEC_TUND							0x728 /* TSEC only */
+#define TSEC_TFRG							0x72c /* TSEC only */
+#define TSEC_CAR1							0x730 /* TSEC only */
+#define TSEC_CAR2							0x734 /* TSEC only */
+#define TSEC_CAM1							0x738 /* TSEC only */
+#define TSEC_CAM2							0x73c /* TSEC only */
+#define TSEC_IADDR0							0x800
+#define TSEC_IADDR1							0x804
+#define TSEC_IADDR2							0x808
+#define TSEC_IADDR3							0x80c
+#define TSEC_IADDR4							0x810
+#define TSEC_IADDR5							0x814
+#define TSEC_IADDR6							0x818
+#define TSEC_IADDR7							0x81c
+#define TSEC_GADDR0							0x880
+#define TSEC_GADDR1							0x884
+#define TSEC_GADDR2							0x888
+#define TSEC_GADDR3							0x88c
+#define TSEC_GADDR4							0x890
+#define TSEC_GADDR5							0x894
+#define TSEC_GADDR6							0x898
+#define TSEC_GADDR7							0x89c
+#define TSEC_ATTR							0xbf8
+#define TSEC_ATTR_ELCWT_NONE					(0<<(31-18))
+#define TSEC_ATTR_ELCWT_ALLOC					(2<<(31-18))
+#define TSEC_ATTR_ELCWT_ALLOC_LOCK				(3<<(31-18))
+#define TSEC_ATTR_BDLWT_NONE					(0<<(31-21))
+#define TSEC_ATTR_BDLWT_ALLOC					(2<<(31-21))
+#define TSEC_ATTR_BDLWT_ALLOC_LOCK				(3<<(31-21))
+#define TSEC_ATTR_RDSEN							(1<<(31-24))
+#define TSEC_ATTR_RBDSEN						(1<<(31-25))
+#define TSEC_ATTRELI						0xbfc
+
+/********** Memory Barriers *********************/
+
+#ifdef __PPC__
+static inline void membarrier(void)
+{
+	asm volatile("sync":::"memory");
+}
+
+#define EIEIO(mem) do { __asm__ volatile("eieio"); } while (0)
+
+#else
+#error "memory barrier not implemented for your CPU architecture"
+#endif
+
+/********** Register Access Primitives **********/
+
+/*
+ * Typedef for base address (uint8_t *) so that
+ * we can do easy pointer arithmetic.
+ */
+typedef volatile uint8_t *FEC_Enet_Base;
+
+/*
+ * All TSEC/FEC registers are 32-bit only.
+ */
+typedef volatile uint32_t FEC_Reg __attribute__((may_alias));
+
+static inline uint32_t
+fec_rd(FEC_Enet_Base b, uint32_t reg)
+{
+#ifdef __BIG_ENDIAN__
+uint32_t rval = *(FEC_Reg *)(b + reg);
+	EIEIO(*(FEC_Reg*)(b+reg));
+	return rval;
+#else
+	return in_be32( (volatile uint32_t*) (b+reg) );
+#endif
+}
+
+static inline void
+fec_wr(FEC_Enet_Base b, uint32_t reg, uint32_t val)
+{
+#ifdef __BIG_ENDIAN__
+	*(FEC_Reg *)(b + reg) = val;
+	EIEIO(*(FEC_Reg*)(b+reg));
+#else
+	out_be32( (volatile uint32_t*) (b+reg), val );
+#endif
+}
+
+/* Set bits in a register */
+static inline void
+fec_set(FEC_Enet_Base b, uint32_t reg, uint32_t val)
+{
+	fec_wr(b, reg, fec_rd(b, reg) | val );
+}
+
+/* Clear bits in a register */
+static inline void
+fec_clr(FEC_Enet_Base b, uint32_t reg, uint32_t val)
+{
+	fec_wr(b, reg, fec_rd(b, reg) & ~val );
+}
+
+/* Clear and set bits in a register */
+static inline void
+fec_csl(FEC_Enet_Base b, uint32_t reg, uint32_t clr, uint32_t set)
+{
+	fec_wr(b, reg, (fec_rd(b, reg) & ~clr) | set);
+}
+
+
+/********** Memory Access Primitives ************/
+
+#ifdef __BIG_ENDIAN__
+static inline uint16_t	ld_be16(volatile uint16_t *a)
+{
+	return *a;
+}
+
+static inline uint32_t	ld_be32(volatile uint32_t *a)
+{
+	return *a;
+}
+
+static inline void	st_be16(volatile uint16_t *a, uint16_t v)
+{
+	*a = v;
+}
+
+static inline void	st_be32(volatile uint32_t *a, uint32_t v)
+{
+	*a = v;
+}
+#else
+#error "ld_be32 & friends not implemented"
+#endif
+
+/********** Note About Coherency ****************/
+
+#ifdef	SW_COHERENCY
+#error 	"SW_COHERENCY not implemented"
+/* Note: maintaining BD coherency in software is not trivial
+ *       because BDs are smaller than a cache line;
+ *       we cannot pad a BD to the length of a cache line because
+ *       the TSEC assumes BDs layed out sequentially in memory.
+ *       We cannot use zero-length BDs to pad to a full cache
+ *       line either because the manual says that the length
+ *       field of a TX BD must not be zero.
+ *
+ *       We probably would need MMU resources to map BDs
+ *       as non-cachable.
+ *
+ *       Maintaining buffer coherency would be easier:
+ *         - make RX buffers cache aligned so we can
+ *           invalidate them w/o overlapping other data.
+ *         - TX buffers may be flushed to memory. If cache
+ *           lines overlap anything else (besides TX buffers)
+ *           then that would only be harmful if (part of) a
+ *           TX buffer would share a cache line with another
+ *           type of DMA buffer that is written by another
+ *           master. Note that BDs have exactly this problem;
+ *           we may not flush one BD because the TSEC could
+ *           have written another BD to memory covered by
+ *           the same cache line.
+ *           This second BD could be lost by a DBCF operation:
+ *            - writes 1st BD to memory OK
+ *            - overwrites 2nd BD with stale data from cache
+ */
+#else
+#define FLUSH_BUF(addr, len) 	do {} while(0)
+#endif
+
+/********** Driver Data Structures **************/
+
+/* Buffer descriptor layout (defined by hardware) */
+struct tsec_bd {
+	volatile uint16_t	flags;
+	volatile uint16_t	len;
+	volatile uint32_t	buf;
+};
+
+typedef struct tsec_bd TSEC_BD __attribute__((aligned(BD_ALIGNMENT)));
+
+/* BD access primitives */
+
+static inline uint16_t  bd_rdfl(TSEC_BD *bd)
+{
+	return ld_be16( & bd->flags );
+}
+
+static inline void bd_wrfl(TSEC_BD *bd, uint16_t v)
+{
+	st_be16( &bd->flags, v );
+}
+
+static inline void bd_setfl(TSEC_BD *bd, uint16_t v)
+{
+	bd_wrfl(bd, bd_rdfl(bd) | v );
+}
+
+static inline void bd_clrfl(TSEC_BD *bd, uint16_t v)
+{
+	bd_wrfl(bd, bd_rdfl(bd) & ~v );
+}
+
+static inline void bd_cslfl(TSEC_BD *bd, uint16_t s, uint16_t c)
+{
+	bd_wrfl( bd, ( bd_rdfl(bd) & ~c ) | s );
+}
+
+static inline uint32_t bd_rdbuf(TSEC_BD *bd)
+{
+	return BUS2CPU_ADDR( ld_be32( &bd->buf ) );
+}
+
+static inline void bd_wrbuf(TSEC_BD *bd, uint32_t addr)
+{
+	st_be32( &bd->buf, CPU2BUS_ADDR(addr) );
+}
+
+/* BD bit definitions */
+
+#define	TSEC_TXBD_R			((uint16_t)(1<<(15- 0)))
+#define TSEC_TXBD_PAD_CRC	((uint16_t)(1<<(15- 1)))
+#define TSEC_TXBD_W			((uint16_t)(1<<(15- 2)))
+#define TSEC_TXBD_I			((uint16_t)(1<<(15- 3)))
+#define TSEC_TXBD_L			((uint16_t)(1<<(15- 4)))
+#define TSEC_TXBD_TC		((uint16_t)(1<<(15- 5)))
+#define TSEC_TXBD_DEF		((uint16_t)(1<<(15- 6)))
+#define TSEC_TXBD_TO1		((uint16_t)(1<<(15- 7)))
+#define TSEC_TXBD_HFE_LC	((uint16_t)(1<<(15- 8)))
+#define TSEC_TXBD_RL		((uint16_t)(1<<(15- 9)))
+#define TSEC_TXBD_RC(x)		((uint16_t)(((x)>>2)&0xf))
+#define TSEC_TXBD_UN		((uint16_t)(1<<(15-14)))
+#define TSEC_TXBD_TXTRUNC	((uint16_t)(1<<(15-15)))
+#define TSEC_TXBD_ERRS		(TSEC_TXBD_RL | TSEC_TXBD_UN | TSEC_TXBD_TXTRUNC)
+
+#define TSEC_RXBD_E			((uint16_t)(1<<(15- 0)))
+#define TSEC_RXBD_RO1		((uint16_t)(1<<(15- 1)))
+#define TSEC_RXBD_W			((uint16_t)(1<<(15- 2)))
+#define TSEC_RXBD_I			((uint16_t)(1<<(15- 3)))
+#define TSEC_RXBD_L			((uint16_t)(1<<(15- 4)))
+#define TSEC_RXBD_F			((uint16_t)(1<<(15- 5)))
+#define TSEC_RXBD_M			((uint16_t)(1<<(15- 7)))
+#define TSEC_RXBD_BC		((uint16_t)(1<<(15- 8)))
+#define TSEC_RXBD_MC		((uint16_t)(1<<(15- 9)))
+#define TSEC_RXBD_LG		((uint16_t)(1<<(15-10)))
+#define TSEC_RXBD_NO		((uint16_t)(1<<(15-11)))
+#define TSEC_RXBD_SH		((uint16_t)(1<<(15-12)))
+#define TSEC_RXBD_CR		((uint16_t)(1<<(15-13)))
+#define TSEC_RXBD_OV		((uint16_t)(1<<(15-14)))
+#define TSEC_RXBD_TR		((uint16_t)(1<<(15-15)))
+
+#define TSEC_RXBD_ERROR	\
+	(TSEC_RXBD_LG | TSEC_RXBD_NO | TSEC_RXBD_SH | TSEC_RXBD_CR | TSEC_RXBD_OV | TSEC_RXBD_TR )
+
+/* Driver 'private' data */
+
+#define NUM_MC_HASHES		256
+
+struct tsec_private {
+	FEC_Enet_Base	base;            /* Controller base address                  */
+	FEC_Enet_Base	phy_base;        /* Phy base address (not necessarily identical
+	                                  * with controller base address);
+	                                  * e.g., phy attached to 2nd controller may be
+									  * connected to mii bus of 1st controller.
+									  */
+	unsigned        phy;             /* Phy address on mii bus                   */
+	unsigned        unit;            /* Driver instance (one-based               */
+	int				isfec;           /* Set if a FEC (not TSEC) controller       */
+	struct tsec_softc *sc;           /* Pointer to BSD driver struct             */
+	TSEC_BD			*ring_area;      /* Not necessarily aligned                  */
+	TSEC_BD			*tx_ring;        /* Aligned array of TX BDs                  */
+	void			**tx_ring_user;  /* Array of user pointers (1 per BD)        */
+	unsigned		tx_ring_size;
+	unsigned		tx_head;         /* first 'dirty' BD; chip is working on     */
+	unsigned		tx_tail;         /* BDs between head and tail                */
+	unsigned		tx_avail;        /* Number of available/free TX BDs          */
+	TSEC_BD			*rx_ring;        /* Aligned array of RX BDs                  */
+	void			**rx_ring_user;  /* Array of user pointers (1 per BD)        */
+	unsigned		rx_tail;         /* Where we left off scanning for full bufs */
+	unsigned		rx_ring_size;
+	void            (*isr)(void*);
+	void            *isr_arg;
+	void			(*cleanup_txbuf) /* Callback to cleanup TX ring              */
+	                  (void*, void*, int);
+	void			*cleanup_txbuf_arg;
+	void			*(*alloc_rxbuf)  /* Callback for allocating RX buffer        */
+	                  (int *psize, uintptr_t *paddr);
+	void			(*consume_rxbuf) /* callback to consume RX buffer            */
+	                  (void*, void*,  int);
+	void			*consume_rxbuf_arg;
+	rtems_id		tid;             /* driver task ID                           */
+	uint32_t		irq_mask;
+	uint32_t		irq_mask_cache;
+	uint32_t		irq_pending;
+	rtems_event_set	event;           /* Task synchronization events              */
+	struct {                         /* Statistics                               */
+		unsigned	xirqs;
+		unsigned	rirqs;
+		unsigned	eirqs;
+		unsigned	lirqs;
+		unsigned	maxchain;
+		unsigned	packet;
+		unsigned	odrops;
+		unsigned	repack;
+		unsigned	eberrs;
+		unsigned	dmarst;
+	}               stats;
+	uint16_t		mc_refcnt[NUM_MC_HASHES];
+};
+
+#define NEXT_TXI(mp, i)	(((i)+1) < (mp)->tx_ring_size ? (i)+1 : 0 )
+#define NEXT_RXI(mp, i)	(((i)+1) < (mp)->rx_ring_size ? (i)+1 : 0 )
+
+/* Stuff needed for bsdnet support */
+struct tsec_bsdsupp {
+	int				oif_flags;					/* old / cached if_flags */
+};
+
+/* bsdnet driver data              */
+struct tsec_softc {
+	struct arpcom		arpcom;
+	struct tsec_bsdsupp	bsd;
+	struct tsec_private	pvt;
+};
+
+/* BSP glue information            */
+typedef struct tsec_bsp_config {
+	uint32_t	  base;
+	int			  xirq, rirq, eirq;
+	uint32_t      phy_base;
+	int           phy_addr;
+} TsecBspConfig;
+
+/********** Global Variables ********************/
+
+/* You may override base addresses
+ * externally - but you must
+ * then also define TSEC_NUM_DRIVER_SLOTS.
+ */
+#ifndef TSEC_CONFIG
+
+static TsecBspConfig tsec_config[] =
+{
+	{
+		base:     BSP_8540_CCSR_BASE         + 0x24000,
+		xirq:     BSP_CORE_IRQ_LOWEST_OFFSET + 13,
+		rirq:     BSP_CORE_IRQ_LOWEST_OFFSET + 14,
+		eirq:     BSP_CORE_IRQ_LOWEST_OFFSET + 18,
+		phy_base: BSP_8540_CCSR_BASE         + 0x24000,
+		phy_addr: 1,
+	},
+	{
+		base:     BSP_8540_CCSR_BASE         + 0x25000,
+		xirq:     BSP_CORE_IRQ_LOWEST_OFFSET + 19,
+		rirq:     BSP_CORE_IRQ_LOWEST_OFFSET + 20,
+		eirq:     BSP_CORE_IRQ_LOWEST_OFFSET + 23,
+		/* all PHYs are on the 1st adapter's mii bus */
+		phy_base: BSP_8540_CCSR_BASE         + 0x24000,
+		phy_addr: 2,
+	},
+};
+
+#define TSEC_CONFIG tsec_config
+
+#endif
+
+#ifndef TSEC_NUM_DRIVER_SLOTS
+#define TSEC_NUM_DRIVER_SLOTS	(sizeof(TSEC_CONFIG)/sizeof(TSEC_CONFIG[0]))
+#endif
+
+/* Driver data structs */
+STATIC struct tsec_softc theTsecEths[TSEC_NUM_DRIVER_SLOTS] = { {{{0}}} };
+
+/* Bsdnet driver task ID; since the BSD stack is single-threaded
+ * there is no point having multiple tasks. A single
+ * task handling all adapters (attached to BSD stack)
+ * is good enough.
+ * Note that an adapter might well be used independently
+ * from the BSD stack (use the low-level driver interface)
+ * and be serviced by a separate task.
+ */
+STATIC rtems_id tsec_tid = 0;
+
+/* If we anticipate using adapters independently
+ * from the BSD stack AND if all PHYs are on a single
+ * adapter's MII bus THEN we must mutex-protect
+ * that MII bus.
+ * If not all of these conditions hold then you
+ * may define TSEC_CONFIG_NO_PHY_REGLOCK and
+ * avoid the creation and use of a mutex.
+ */
+#ifndef TSEC_CONFIG_NO_PHY_REGLOCK
+/*
+ * PHY register access protection mutex;
+ * multiple instances of tsec hardware
+ * may share e.g., the first tsec's registers
+ * for accessing the mii bus where all PHYs
+ * may be connected. If we would only deal
+ * with BSD networking then using the normal
+ * networking semaphore would be OK. However,
+ * we want to support standalone drivers and
+ * therefore might require a separate lock.
+ */
+STATIC rtems_id tsec_mtx = 0;
+#define REGLOCK()	do { \
+		if ( RTEMS_SUCCESSFUL != rtems_semaphore_obtain(tsec_mtx, RTEMS_WAIT, RTEMS_NO_TIMEOUT) ) \
+			rtems_panic(DRVNAME": unable to lock phy register protection mutex"); \
+		} while (0)
+#define REGUNLOCK()	rtems_semaphore_release(tsec_mtx)
+#else
+#define REGLOCK()	do { } while (0)
+#define REGUNLOCK()	do { } while (0)
+#endif
+
+static void tsec_xisr(rtems_irq_hdl_param arg);
+static void tsec_risr(rtems_irq_hdl_param arg);
+static void tsec_eisr(rtems_irq_hdl_param arg);
+static void tsec_lisr(rtems_irq_hdl_param arg);
+
+static void noop(const rtems_irq_connect_data *unused)  {            }
+static int  nopf(const rtems_irq_connect_data *unused)  { return -1; }
+
+/********** Low-level Driver API ****************/
+
+/*
+ * This API provides driver access to applications that
+ * want to use e.g., the second ethernet interface
+ * independently from the BSD TCP/IP stack. E.g., for
+ * raw ethernet packet communication...
+ */
+
+/*
+ * Descriptor scavenger; cleanup the TX ring, passing all buffers
+ * that have been sent to the cleanup_tx() callback.
+ * This routine is called from BSP_tsec_send_buf(), BSP_tsec_init_hw(),
+ * BSP_tsec_stop_hw().
+ *
+ * RETURNS: number of buffers processed.
+ */
+
+int
+BSP_tsec_swipe_tx(struct tsec_private *mp)
+{
+int						rval = 0;
+int                     i;
+TSEC_BD					*bd;
+uint16_t				flags;
+void                   *u;
+
+#if DEBUG > 2
+printf("Swipe TX entering:\n");
+tsec_dump_tring(mp);
+#endif
+
+	for ( i = mp->tx_head; bd_rdbuf( (bd = &mp->tx_ring[i]) ); i = NEXT_TXI(mp, i) ) {
+
+		flags = bd_rdfl( bd );
+		if ( (TSEC_TXBD_R & flags) ) {
+			/* nothing more to clean */
+			break;
+		}
+
+		/* tx_ring_user[i] is only set on the last descriptor in a chain;
+		 * we only count errors in the last descriptor;
+		 */
+		if ( (u=mp->tx_ring_user[i]) ) {
+			mp->cleanup_txbuf(u, mp->cleanup_txbuf_arg, (flags & TSEC_TXBD_ERRS));
+			mp->tx_ring_user[i] = 0;
+		}
+
+		bd_wrbuf( bd, 0 );
+
+		mp->tx_avail++;
+
+		rval++;
+	}
+	mp->tx_head = i;
+
+#if DEBUG > 2
+tsec_dump_tring(mp);
+printf("Swipe TX leaving\n");
+#endif
+
+	return rval;
+}
+
+
+/*
+ * Reset the controller and bring into a known state;
+ * all interrupts are off
+ */
+STATIC void
+tsec_reset_hw(struct tsec_private *mp)
+{
+FEC_Enet_Base b = mp->base;
+
+	/* Make sure all interrupts are off */
+	fec_wr(b, TSEC_IMASK, TSEC_IMASK_NONE);
+
+#ifndef TSEC_CONFIG_NO_PHY_REGLOCK
+	/* don't bother disabling irqs in the PHY if this is
+	 * called before the mutex is created;
+	 * the PHY ISR is not hooked yet and there can be no
+	 * interrupts...
+	 */
+	if ( tsec_mtx )
+#endif
+	phy_dis_irq_at_phy( mp );
+
+	mp->irq_mask_cache = 0;
+
+	/* Follow the manual resetting the chip */
+
+	/* Do graceful stop (if not in stop condition already) */
+	if ( ! (TSEC_DMACTRL_GTS & fec_rd(b, TSEC_DMACTRL)) ) {
+		/* Make sure GTSC is clear */
+		fec_wr(b, TSEC_IEVENT, TSEC_IEVENT_GTSC);
+		fec_set(b, TSEC_DMACTRL, TSEC_DMACTRL_GTS);
+		while ( ! (TSEC_IEVENT_GTSC & fec_rd(b, TSEC_IEVENT)) )
+			/* wait */;
+	}
+
+	/* Clear RX/TX enable in MAC */
+	fec_clr(b, TSEC_MACCFG1, TSEC_MACCFG1_RX_EN | TSEC_MACCFG1_TX_EN);
+
+	/* wait for > 8ms */
+	rtems_task_wake_after(1);
+
+	/* set GRS if not already stopped */
+	if ( ! (TSEC_DMACTRL_GRS & fec_rd(b, TSEC_DMACTRL)) ) {
+		/* Make sure GRSC is clear */
+		fec_wr(b, TSEC_IEVENT, TSEC_IEVENT_GRSC);
+		fec_set(b, TSEC_DMACTRL, TSEC_DMACTRL_GRS);
+		while ( ! (TSEC_IEVENT_GRSC & fec_rd(b, TSEC_IEVENT)) )
+			/* wait */;
+	}
+
+	fec_set(b, TSEC_MACCFG1, TSEC_MACCFG1_SOFT_RESET);
+	fec_clr(b, TSEC_MACCFG1, TSEC_MACCFG1_SOFT_RESET);
+
+	/* clear all irqs */
+	fec_wr (b, TSEC_IEVENT, TSEC_IEVENT_ALL);
+}
+
+/* Helper to hook/unhook interrupts */
+
+static void
+install_remove_isrs(int install, struct tsec_private *mp, uint32_t irq_mask)
+{
+	rtems_irq_connect_data xxx;
+	int                    installed = 0;
+	int                    line;
+	int                    unit = mp->unit;
+
+	xxx.on     = noop;
+	xxx.off    = noop;
+	xxx.isOn   = nopf;
+	xxx.handle = mp;
+
+	if ( irq_mask & TSEC_TXIRQ ) {
+		xxx.name = TSEC_CONFIG[unit-1].xirq;
+		xxx.hdl  = tsec_xisr;
+		if ( ! (install ?
+				BSP_install_rtems_irq_handler( &xxx ) :
+				BSP_remove_rtems_irq_handler( &xxx ) ) ) {
+			rtems_panic(DRVNAME": Unable to install TX ISR\n");
+		}
+		installed++;
+	}
+
+	if ( (irq_mask & TSEC_RXIRQ) ) {
+		if ( (line = TSEC_CONFIG[unit-1].rirq) < 0 && ! installed ) {
+			/* have no dedicated RX IRQ line; install TX ISR if not already done */
+			line = TSEC_CONFIG[unit-1].xirq;
+		}
+		xxx.name = line;
+		xxx.hdl  = tsec_risr;
+		if ( ! (install ?
+				BSP_install_rtems_irq_handler( &xxx ) :
+				BSP_remove_rtems_irq_handler( &xxx ) ) ) {
+			rtems_panic(DRVNAME": Unable to install RX ISR\n");
+		}
+		installed++;
+	}
+
+	if ( (line = TSEC_CONFIG[unit-1].eirq) < 0 && ! installed ) {
+		/* have no dedicated RX IRQ line; install TX ISR if not already done */
+		line = TSEC_CONFIG[unit-1].xirq;
+	}
+	xxx.name = line;
+	xxx.hdl  = tsec_eisr;
+	if ( ! (install ?
+			BSP_install_rtems_irq_handler( &xxx ) :
+			BSP_remove_rtems_irq_handler( &xxx ) ) ) {
+		rtems_panic(DRVNAME": Unable to install ERR ISR\n");
+	}
+
+	if ( irq_mask & TSEC_LINK_INTR ) {
+		phy_init_irq( install, mp, tsec_lisr );
+	}
+}
+
+/*
+ * Setup an interface.
+ * Allocates resources for descriptor rings and sets up the driver software structure.
+ *
+ * Arguments:
+ *	unit:
+ *		interface # (1..2). The interface must not be attached to BSD already.
+ *
+ *  driver_tid:
+ *		ISR posts RTEMS event # ('unit' - 1) to task with ID 'driver_tid' and disables interrupts
+ *		from this interface.
+ *
+ *	void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred):
+ *		Pointer to user-supplied callback to release a buffer that had been sent
+ *		by BSP_tsec_send_buf() earlier. The callback is passed 'cleanup_txbuf_arg'
+ *		and a flag indicating whether the send had been successful.
+ *		The driver no longer accesses 'user_buf' after invoking this callback.
+ *		CONTEXT: This callback is executed either by BSP_tsec_swipe_tx() or
+ *		BSP_tsec_send_buf(), BSP_tsec_init_hw(), BSP_tsec_stop_hw() (the latter
+ *		ones calling BSP_tsec_swipe_tx()).
+ *	void *cleanup_txbuf_arg:
+ *		Closure argument that is passed on to 'cleanup_txbuf()' callback;
+ *
+ *	void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr),
+ *		Pointer to user-supplied callback to allocate a buffer for subsequent
+ *		insertion into the RX ring by the driver.
+ *		RETURNS: opaque handle to the buffer (which may be a more complex object
+ *				 such as an 'mbuf'). The handle is not used by the driver directly
+ *				 but passed back to the 'consume_rxbuf()' callback.
+ *				 Size of the available data area and pointer to buffer's data area
+ *				 in '*psize' and '*p_data_area', respectively.
+ *				 If no buffer is available, this routine should return NULL in which
+ *				 case the driver drops the last packet and re-uses the last buffer
+ *				 instead of handing it out to 'consume_rxbuf()'.
+ *		CONTEXT: Called when initializing the RX ring (BSP_tsec_init_hw()) or when
+ *				 swiping it (BSP_tsec_swipe_rx()).
+ *
+ *
+ *	void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len);
+ *		Pointer to user-supplied callback to pass a received buffer back to
+ *		the user. The driver no longer accesses the buffer after invoking this
+ *		callback (with 'len'>0, see below). 'user_buf' is the buffer handle
+ *		previously generated by 'alloc_rxbuf()'.
+ *		The callback is passed 'cleanup_rxbuf_arg' and a 'len'
+ *		argument giving the number of bytes that were received.
+ *		'len' may be <=0 in which case the 'user_buf' argument is NULL.
+ *		'len' == 0 means that the last 'alloc_rxbuf()' had failed,
+ *		'len' < 0 indicates a receiver error. In both cases, the last packet
+ *		was dropped/missed and the last buffer will be re-used by the driver.
+ *		NOTE: the data are 'prefixed' with two bytes, i.e., the ethernet packet header
+ *		      is stored at offset 2 in the buffer's data area. Also, the FCS (4 bytes)
+ *		      is appended. 'len' accounts for both.
+ *		CONTEXT: Called from BSP_tsec_swipe_rx().
+ *	void *cleanup_rxbuf_arg:
+ *		Closure argument that is passed on to 'consume_rxbuf()' callback;
+ *
+ *  rx_ring_size, tx_ring_size:
+ *		How many big to make the RX and TX descriptor rings. Note that the sizes
+ *		may be 0 in which case a reasonable default will be used.
+ *		If either ring size is < 0 then the RX or TX will be disabled.
+ *		Note that it is illegal in this case to use BSP_tsec_swipe_rx() or
+ *		BSP_tsec_swipe_tx(), respectively.
+ *
+ *  irq_mask:
+ *		Interrupts to enable. OR of flags from above.
+ *
+ */
+
+static struct tsec_private *
+tsec_setup_internal(
+	int		 unit,
+	rtems_id driver_tid,
+	void     (*isr)(void *),
+	void *   isr_arg,
+	void     (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred),
+	void *   cleanup_txbuf_arg,
+	void *   (*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr),
+	void     (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len),
+	void *   consume_rxbuf_arg,
+	int		 rx_ring_size,
+	int		 tx_ring_size,
+	int		 irq_mask
+)
+{
+struct tsec_private *mp;
+int                  i;
+struct ifnet         *ifp;
+
+	if ( unit <= 0 || unit > TSEC_NUM_DRIVER_SLOTS ) {
+		printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, TSEC_NUM_DRIVER_SLOTS);
+		return 0;
+	}
+
+	ifp = &theTsecEths[unit-1].arpcom.ac_if;
+	if ( ifp->if_init ) {
+		if ( ifp->if_init ) {
+			printk(DRVNAME": instance %i already attached.\n", unit);
+			return 0;
+		}
+	}
+
+
+	if ( rx_ring_size < 0 && tx_ring_size < 0 )
+		return 0;
+
+	mp                    = &theTsecEths[unit - 1].pvt;
+
+	memset(mp, 0, sizeof(*mp));
+
+	mp->sc                = &theTsecEths[unit - 1];
+	mp->unit              = unit;
+
+	mp->base              = (FEC_Enet_Base)TSEC_CONFIG[unit-1].base;
+	mp->phy_base          = (FEC_Enet_Base)TSEC_CONFIG[unit-1].phy_base;
+	mp->phy               = TSEC_CONFIG[unit-1].phy_addr;
+	mp->tid               = driver_tid;
+	/* use odd event flags for link status IRQ */
+	mp->event             = TSEC_ETH_EVENT((unit-1));
+
+	mp->cleanup_txbuf     = cleanup_txbuf;
+	mp->cleanup_txbuf_arg = cleanup_txbuf_arg;
+	mp->alloc_rxbuf       = alloc_rxbuf;
+	mp->consume_rxbuf     = consume_rxbuf;
+	mp->consume_rxbuf_arg = consume_rxbuf_arg;
+
+	/* stop hw prior to setting ring-size to anything nonzero
+	 * so that the rings are not swept.
+	 */
+	BSP_tsec_stop_hw(mp);
+
+	if ( 0 == rx_ring_size )
+		rx_ring_size = TSEC_RX_RING_SIZE;
+	if ( 0 == tx_ring_size )
+		tx_ring_size = TSEC_TX_RING_SIZE;
+
+	mp->rx_ring_size = rx_ring_size < 0 ? 0 : rx_ring_size;
+	mp->tx_ring_size = tx_ring_size < 0 ? 0 : tx_ring_size;
+
+	/* allocate ring area; add 1 entry -- room for alignment */
+	assert( !mp->ring_area );
+	mp->ring_area = malloc(
+						sizeof(*mp->ring_area) *
+							(mp->rx_ring_size + mp->tx_ring_size + 1),
+						M_DEVBUF,
+						M_WAIT );
+	assert( mp->ring_area );
+
+	mp->tx_ring_user = malloc( sizeof(*mp->tx_ring_user) *
+							(mp->rx_ring_size + mp->tx_ring_size),
+						M_DEVBUF,
+						M_WAIT );
+	assert( mp->tx_ring_user );
+
+	mp->rx_ring_user = mp->tx_ring_user + mp->tx_ring_size;
+
+	/* Initialize TX ring */
+	mp->tx_ring = (TSEC_BD *) ALIGNTO(mp->ring_area,BD_ALIGNMENT);
+
+	mp->rx_ring = mp->tx_ring + mp->tx_ring_size;
+
+	for ( i=0; i<mp->tx_ring_size; i++ ) {
+		bd_wrbuf( &mp->tx_ring[i], 0 );
+		bd_wrfl( &mp->tx_ring[i], TSEC_TXBD_I );
+		mp->tx_ring_user[i] = 0;
+	}
+	/* set wrap-around flag on last BD */
+	if ( mp->tx_ring_size )
+		bd_setfl( &mp->tx_ring[i-1], TSEC_TXBD_W );
+
+	mp->tx_tail = mp->tx_head = 0;
+	mp->tx_avail = mp->tx_ring_size;
+
+	/* Initialize RX ring (buffers are allocated later) */
+	for ( i=0; i<mp->rx_ring_size; i++ ) {
+		bd_wrbuf( &mp->rx_ring[i], 0 );
+		bd_wrfl( &mp->rx_ring[i], TSEC_RXBD_I );
+		mp->rx_ring_user[i] = 0;
+	}
+	/* set wrap-around flag on last BD */
+	if ( mp->rx_ring_size )
+		bd_setfl( &mp->rx_ring[i-1], TSEC_RXBD_W );
+
+	if ( irq_mask ) {
+		if ( rx_ring_size == 0 )
+			irq_mask &= ~ TSEC_RXIRQ;
+		if ( tx_ring_size == 0 )
+			irq_mask &= ~ TSEC_TXIRQ;
+	}
+
+#ifndef TSEC_CONFIG_NO_PHY_REGLOCK
+	if ( ! tsec_mtx ) {
+		rtems_status_code     sc;
+		rtems_id              new_mtx;
+		rtems_interrupt_level l;
+		sc = rtems_semaphore_create(
+				rtems_build_name('t','s','e','X'),
+				1,
+				RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY | RTEMS_DEFAULT_ATTRIBUTES,
+				0,
+				&new_mtx);
+		if ( RTEMS_SUCCESSFUL != sc ) {
+			rtems_error(sc,DRVNAME": creating mutex\n");
+			rtems_panic("unable to proceed\n");
+		}
+		rtems_interrupt_disable( l );
+			if ( ! tsec_mtx ) {
+				tsec_mtx = new_mtx;
+				new_mtx  = 0;
+			}
+		rtems_interrupt_enable( l );
+
+		if ( new_mtx ) {
+			/* another task was faster installing the mutex */
+			rtems_semaphore_delete( new_mtx );
+		}
+
+	}
+#endif
+
+	if ( irq_mask ) {
+		install_remove_isrs( 1, mp, irq_mask );
+	}
+
+	mp->irq_mask = irq_mask;
+
+	/* mark as used */
+	ifp->if_init = (void*)(-1);
+
+	return mp;
+}
+
+struct tsec_private *
+BSP_tsec_setup(
+	int		 unit,
+	rtems_id driver_tid,
+	void     (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred),
+	void *   cleanup_txbuf_arg,
+	void *   (*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr),
+	void     (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len),
+	void *   consume_rxbuf_arg,
+	int		 rx_ring_size,
+	int		 tx_ring_size,
+	int		 irq_mask
+)
+{
+	if ( irq_mask && ! driver_tid ) {
+		printk(DRVNAME": must supply a TID if irq_mask not zero\n");
+		return 0;
+	}
+	return tsec_setup_internal(
+								unit,
+								driver_tid,
+								0, 0,
+								cleanup_txbuf, cleanup_txbuf_arg,
+								alloc_rxbuf,
+								consume_rxbuf, consume_rxbuf_arg,
+								rx_ring_size,
+								tx_ring_size,
+								irq_mask
+							   );
+}
+
+struct tsec_private *
+BSP_tsec_setup_1(
+	int		 unit,
+	void     (*isr)(void*),
+	void *   isr_arg,
+	void     (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred),
+	void *   cleanup_txbuf_arg,
+	void *   (*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr),
+	void     (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len),
+	void *   consume_rxbuf_arg,
+	int		 rx_ring_size,
+	int		 tx_ring_size,
+	int		 irq_mask
+)
+{
+	if ( irq_mask && ! isr ) {
+		printk(DRVNAME": must supply a ISR if irq_mask not zero\n");
+		return 0;
+	}
+	return tsec_setup_internal(
+								unit,
+								0,
+								isr, isr_arg,
+								cleanup_txbuf, cleanup_txbuf_arg,
+								alloc_rxbuf,
+								consume_rxbuf, consume_rxbuf_arg,
+								rx_ring_size,
+								tx_ring_size,
+								irq_mask
+							   );
+}
+
+void
+BSP_tsec_reset_stats(struct tsec_private *mp)
+{
+FEC_Enet_Base b = mp->base;
+int i;
+	memset( &mp->stats, 0, sizeof( mp->stats ) );
+	if ( mp->isfec )
+		return;
+	for ( i=TSEC_TR64; i<=TSEC_TFRG; i+=4 )
+		fec_wr( b, i, 0 );
+
+}
+
+/*
+ * retrieve media status from the PHY
+ * and set duplex mode in MACCFG2 based
+ * on the result.
+ *
+ * RETURNS: media word (or -1 if BSP_tsec_media_ioctl() fails)
+ */
+static int
+mac_set_duplex(struct tsec_private *mp)
+{
+int media = IFM_MAKEWORD(0, 0, 0, 0);
+
+	if ( 0 == BSP_tsec_media_ioctl(mp, SIOCGIFMEDIA, &media)) {
+		if ( IFM_LINK_OK & media ) {
+			/* update duplex setting in MACCFG2 */
+			if ( IFM_FDX & media ) {
+				fec_set( mp->base, TSEC_MACCFG2, TSEC_MACCFG2_FD );
+			} else {
+				fec_clr( mp->base, TSEC_MACCFG2, TSEC_MACCFG2_FD );
+			}
+		}
+		return media;
+	}
+	return -1;
+}
+
+/*
+ * Initialize interface hardware
+ *
+ * 'mp'			handle obtained by from BSP_tsec_setup().
+ * 'promisc'	whether to set promiscuous flag.
+ * 'enaddr'		pointer to six bytes with MAC address. Read
+ *				from the device if NULL.
+ */
+void
+BSP_tsec_init_hw(struct tsec_private *mp, int promisc, unsigned char *enaddr)
+{
+FEC_Enet_Base b = mp->base;
+unsigned      i;
+uint32_t      v;
+int           sz;
+
+	BSP_tsec_stop_hw(mp);
+
+#ifdef PARANOIA
+	assert( mp->tx_avail == mp->tx_ring_size );
+	assert( mp->tx_head  == mp->tx_tail );
+	for ( i=0; i<mp->tx_ring_size; i++ ) {
+		assert( mp->tx_ring_user[i] == 0 );
+	}
+#endif
+
+	/* make sure RX ring is filled */
+	for ( i=0; i<mp->rx_ring_size; i++ ) {
+		uintptr_t data_area;
+		if ( ! (mp->rx_ring_user[i] = mp->alloc_rxbuf( &sz, &data_area)) ) {
+			rtems_panic(DRVNAME": unable to fill RX ring");
+		}
+		if ( data_area & (RX_BUF_ALIGNMENT-1) )
+			rtems_panic(DRVNAME": RX buffers must be %i-byte aligned", RX_BUF_ALIGNMENT);
+
+		bd_wrbuf( &mp->rx_ring[i], data_area );
+		st_be16 ( &mp->rx_ring[i].len, sz        );
+		bd_setfl( &mp->rx_ring[i], TSEC_RXBD_E | TSEC_RXBD_I );
+	}
+
+	mp->tx_tail = mp->tx_head = 0;
+
+	mp->rx_tail = 0;
+
+	/* tell chip what the ring areas are */
+	fec_wr( b, TSEC_TBASE, (uint32_t)mp->tx_ring );
+	fec_wr( b, TSEC_RBASE, (uint32_t)mp->rx_ring );
+
+	/* clear and disable IRQs */
+	fec_wr( b, TSEC_IEVENT, TSEC_IEVENT_ALL );
+	fec_wr( b, TSEC_IMASK,  TSEC_IMASK_NONE );
+	mp->irq_mask_cache = 0;
+
+	/* bring other regs. into a known state */
+	fec_wr( b, TSEC_EDIS,   0 );
+
+	if ( !mp->isfec )
+		fec_wr( b, TSEC_ECNTRL, TSEC_ECNTRL_CLRCNT | TSEC_ECNTRL_STEN );
+
+	fec_wr( b, TSEC_MINFLR, 64 );
+	fec_wr( b, TSEC_PTV,     0 );
+
+	v = TSEC_DMACTRL_WWR;
+
+	if ( mp->tx_ring_size )
+		v |= TSEC_DMACTRL_TDSEN | TSEC_DMACTRL_TBDSEN | TSEC_DMACTRL_WOP;
+
+	fec_wr( b, TSEC_DMACTRL, v );
+
+	fec_wr( b, TSEC_FIFO_PAUSE_CTRL,           0 );
+	fec_wr( b, TSEC_FIFO_TX_THR,             256 );
+	fec_wr( b, TSEC_FIFO_TX_STARVE,          128 );
+	fec_wr( b, TSEC_FIFO_TX_STARVE_SHUTOFF,  256 );
+	fec_wr( b, TSEC_TCTRL,                     0 );
+	if ( !mp->isfec ) {
+		/* FIXME: use IRQ coalescing ? not sure how to
+		 * set the timer (bad if it depends on the speed
+		 * setting).
+		 */
+		fec_wr( b, TSEC_TXIC, 0);
+	}
+	fec_wr( b, TSEC_OSTBD,                     0 );
+	fec_wr( b, TSEC_RCTRL, (promisc ? TSEC_RCTRL_PROM : 0) );
+	fec_wr( b, TSEC_RSTAT,       TSEC_RSTAT_QHLT );
+	if ( !mp->isfec ) {
+		/* FIXME: use IRQ coalescing ? not sure how to
+		 * set the timer (bad if it depends on the speed
+		 * setting).
+		 */
+		fec_wr( b, TSEC_RXIC, 0 );
+	}
+	fec_wr( b, TSEC_MRBLR,              sz & ~63 );
+
+	/* Reset config. as per manual */
+	fec_wr( b, TSEC_IPGIFG,           0x40605060 );
+	fec_wr( b, TSEC_HAFDUP,           0x00a1f037 );
+	fec_wr( b, TSEC_MAXFRM,                 1536 );
+
+	if ( enaddr ) {
+		union {
+			uint32_t u;
+			uint16_t s[2];
+			uint8_t  c[4];
+		} x;
+		fec_wr( b, TSEC_MACSTNADDR1, ld_le32( (volatile uint32_t*)(enaddr + 2) ) );
+		x.s[0] = ld_le16( (volatile uint16_t *)(enaddr) );
+		fec_wr( b, TSEC_MACSTNADDR2, x.u );
+	}
+
+	for ( i=0; i<8*4; i+=4 ) {
+		fec_wr( b, TSEC_IADDR0 + i, 0 );
+	}
+
+	BSP_tsec_mcast_filter_clear(mp);
+
+	BSP_tsec_reset_stats(mp);
+
+	fec_wr( b, TSEC_ATTR, (TSEC_ATTR_RDSEN | TSEC_ATTR_RBDSEN) );
+	fec_wr( b, TSEC_ATTRELI,        0 );
+
+	/* The interface type is probably board dependent; leave alone...
+	v  = mp->isfec ? TSEC_MACCFG2_IF_MODE_MII : TSEC_MACCFG2_IF_MODE_GMII;
+	*/
+
+	fec_clr( b, TSEC_MACCFG2,
+			  TSEC_MACCFG2_PREAMBLE_15
+			| TSEC_MACCFG2_HUGE_FRAME
+			| TSEC_MACCFG2_LENGTH_CHECK );
+
+	fec_set( b, TSEC_MACCFG2,
+			  TSEC_MACCFG2_PREAMBLE_7
+			| TSEC_MACCFG2_PAD_CRC );
+
+	mac_set_duplex( mp );
+
+	v = 0;
+	if ( mp->rx_ring_size ) {
+		v |= TSEC_MACCFG1_RX_EN;
+	}
+	if ( mp->tx_ring_size ) {
+		v |= TSEC_MACCFG1_TX_EN;
+	}
+
+	fec_wr( b, TSEC_MACCFG1, v);
+
+	/* The following sequency (FWIW) ensures that
+	 *
+	 * - PHY and TSEC interrupts are enabled atomically
+	 * - IRQS are not globally off while accessing the PHY
+	 *   (slow MII)
+	 */
+
+	if ( (TSEC_LINK_INTR & mp->irq_mask) ) {
+		/* disable PHY irq at PIC (fast) */
+		phy_dis_irq( mp );
+		/* enable PHY irq (MII operation, slow) */
+		phy_en_irq_at_phy (mp );
+	}
+
+	BSP_tsec_enable_irq_mask( mp, mp->irq_mask );
+}
+
+static void
+hash_prog(struct tsec_private *mp, uint32_t tble, const uint8_t *enaddr, int accept)
+{
+uint8_t  s;
+uint32_t reg, bit;
+
+	s = ether_crc32_le(enaddr, ETHER_ADDR_LEN);
+
+	/* bit-reverse */
+    s = ((s&0x0f) << 4) | ((s&0xf0) >> 4);
+    s = ((s&0x33) << 2) | ((s&0xcc) >> 2);
+    s = ((s&0x55) << 1) | ((s&0xaa) >> 1);
+
+	reg = tble + ((s >> (5-2)) & ~3);
+	bit = 1 << (31 - (s & 31));
+
+	if ( accept ) {
+		if ( 0 == mp->mc_refcnt[s]++ )
+			fec_set( mp->base, reg, bit );
+	} else {
+		if ( mp->mc_refcnt[s] > 0 && 0 == --mp->mc_refcnt[s] )
+			fec_clr( mp->base, reg, bit );
+	}
+}
+
+void
+BSP_tsec_mcast_filter_clear(struct tsec_private *mp)
+{
+int i;
+	for ( i=0; i<8*4; i+=4 ) {
+		fec_wr( mp->base, TSEC_GADDR0 + i, 0 );
+	}
+	for ( i=0; i<NUM_MC_HASHES; i++ )
+		mp->mc_refcnt[i] = 0;
+}
+
+void
+BSP_tsec_mcast_filter_accept_all(struct tsec_private *mp)
+{
+int i;
+	for ( i=0; i<8*4; i+=4 ) {
+		fec_wr( mp->base, TSEC_GADDR0 + i, 0xffffffff );
+	}
+	for ( i=0; i<NUM_MC_HASHES; i++ )
+		mp->mc_refcnt[i]++;
+}
+
+static void
+mcast_filter_prog(struct tsec_private *mp, uint8_t *enaddr, int accept)
+{
+static const uint8_t bcst[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+	if ( ! (enaddr[0] & 0x01) ) {
+		/* not a multicast address; ignore */
+		return;
+	}
+	if ( 0 == memcmp( enaddr, bcst, sizeof(bcst) ) ) {
+		/* broadcast; ignore */
+		return;
+	}
+	hash_prog(mp, TSEC_GADDR0, enaddr, accept);
+}
+
+void
+BSP_tsec_mcast_filter_accept_add(struct tsec_private *mp, uint8_t *enaddr)
+{
+	mcast_filter_prog(mp, enaddr, 1 /* accept */);
+}
+
+void
+BSP_tsec_mcast_filter_accept_del(struct tsec_private *mp, uint8_t *enaddr)
+{
+	mcast_filter_prog(mp, enaddr, 0 /* delete */);
+}
+
+void
+BSP_tsec_dump_stats(struct tsec_private *mp, FILE *f)
+{
+FEC_Enet_Base b;
+
+	if ( !mp )
+		mp = &theTsecEths[0].pvt;
+
+	if ( ! f )
+		f = stdout;
+
+	fprintf(f, DRVNAME"%i Statistics:\n", mp->unit);
+
+    b = mp->base;
+
+	fprintf(f, "TX  IRQS: %u\n", mp->stats.xirqs);
+	fprintf(f, "RX  IRQS: %u\n", mp->stats.rirqs);
+	fprintf(f, "ERR IRQS: %u\n", mp->stats.eirqs);
+	fprintf(f, "bus errs: %u\n", mp->stats.eberrs);
+	fprintf(f, "dmawhack: %u\n", mp->stats.dmarst);
+	fprintf(f, "LNK IRQS: %u\n", mp->stats.lirqs);
+	fprintf(f, "maxchain: %u\n", mp->stats.maxchain);
+	fprintf(f, "xpackets: %u\n", mp->stats.packet);
+	fprintf(f, "odrops:   %u\n", mp->stats.odrops);
+	fprintf(f, "repack:   %u\n", mp->stats.repack);
+
+	if ( mp->isfec ) {
+		fprintf(f,"FEC has no HW counters\n");
+		return;
+	}
+
+	fprintf(f,"TSEC MIB counters (modulo 2^32):\n");
+
+	fprintf(f,"RX bytes     %"PRIu32"\n", fec_rd( b, TSEC_RBYT ));
+	fprintf(f,"RX      pkts %"PRIu32"\n", fec_rd( b, TSEC_RPKT ));
+	fprintf(f,"RX FCS errs  %"PRIu32"\n", fec_rd( b, TSEC_RFCS ));
+	fprintf(f,"RX mcst pkts %"PRIu32"\n", fec_rd( b, TSEC_RMCA ));
+	fprintf(f,"RX bcst pkts %"PRIu32"\n", fec_rd( b, TSEC_RBCA ));
+	fprintf(f,"RX pse frms  %"PRIu32"\n", fec_rd( b, TSEC_RXPF ));
+	fprintf(f,"RX drop      %"PRIu32"\n", fec_rd( b, TSEC_RDRP ));
+	fprintf(f,"TX bytes     %"PRIu32"\n", fec_rd( b, TSEC_TBYT ));
+	fprintf(f,"TX      pkts %"PRIu32"\n", fec_rd( b, TSEC_TPKT ));
+	fprintf(f,"TX FCS errs  %"PRIu32"\n", fec_rd( b, TSEC_TFCS ));
+	fprintf(f,"TX mcst pkts %"PRIu32"\n", fec_rd( b, TSEC_TMCA ));
+	fprintf(f,"TX bcst pkts %"PRIu32"\n", fec_rd( b, TSEC_TBCA ));
+	fprintf(f,"TX pse frms  %"PRIu32"\n", fec_rd( b, TSEC_TXPF ));
+	fprintf(f,"TX drop      %"PRIu32"\n", fec_rd( b, TSEC_TDRP ));
+	fprintf(f,"TX coll      %"PRIu32"\n", fec_rd( b, TSEC_TSCL ));
+	fprintf(f,"TX mcoll     %"PRIu32"\n", fec_rd( b, TSEC_TMCL ));
+	fprintf(f,"TX late coll %"PRIu32"\n", fec_rd( b, TSEC_TLCL ));
+	fprintf(f,"TX exc coll  %"PRIu32"\n", fec_rd( b, TSEC_TXCL ));
+	fprintf(f,"TX defer     %"PRIu32"\n", fec_rd( b, TSEC_TDFR ));
+	fprintf(f,"TX exc defer %"PRIu32"\n", fec_rd( b, TSEC_TEDF ));
+	fprintf(f,"TX oversz    %"PRIu32"\n", fec_rd( b, TSEC_TOVR ));
+	fprintf(f,"TX undersz   %"PRIu32"\n", fec_rd( b, TSEC_TUND ));
+}
+
+/*
+ * Shutdown hardware and clean out the rings
+ */
+void
+BSP_tsec_stop_hw(struct tsec_private *mp)
+{
+unsigned i;
+	/* stop and reset hardware */
+	tsec_reset_hw( mp );
+
+	if ( mp->tx_ring_size ) {
+		/* should be OK to clear all ownership flags */
+		for ( i=0; i<mp->tx_ring_size; i++ ) {
+			bd_clrfl( &mp->tx_ring[i], TSEC_TXBD_R );
+		}
+		BSP_tsec_swipe_tx(mp);
+#if DEBUG > 0
+		tsec_dump_tring(mp);
+		fflush(stderr); fflush(stdout);
+#endif
+#ifdef PARANOIA
+		assert( mp->tx_avail == mp->tx_ring_size );
+		assert( mp->tx_head  == mp->tx_tail      );
+		for ( i=0; i<mp->tx_ring_size; i++ ) {
+			assert( !bd_rdbuf( & mp->tx_ring[i] ) );
+			assert( !mp->tx_ring_user[i] );
+		}
+#endif
+	}
+
+	if ( mp->rx_ring_size ) {
+		for ( i=0; i<mp->rx_ring_size; i++ ) {
+			bd_clrfl( &mp->rx_ring[i], TSEC_RXBD_E );
+			bd_wrbuf( &mp->rx_ring[i], 0 );
+			if ( mp->rx_ring_user[i] )
+				mp->consume_rxbuf( mp->rx_ring_user[i], mp->consume_rxbuf_arg, 0 );
+			mp->rx_ring_user[i] = 0;
+		}
+	}
+}
+
+/*
+ * calls BSP_tsec_stop_hw(), releases all resources and marks the interface
+ * as unused.
+ * RETURNS 0 on success, nonzero on failure.
+ * NOTE:   the handle MUST NOT be used after successful execution of this
+ *         routine.
+ */
+int
+BSP_tsec_detach(struct tsec_private *mp)
+{
+
+	if ( ! mp || !mp->sc || ! mp->sc->arpcom.ac_if.if_init ) {
+		fprintf(stderr,"Unit not setup -- programming error!\n");
+		return -1;
+	}
+
+	BSP_tsec_stop_hw(mp);
+
+	install_remove_isrs( 0, mp, mp->irq_mask );
+
+	free( (void*)mp->ring_area, M_DEVBUF );
+	free( (void*)mp->tx_ring_user, M_DEVBUF );
+	memset(mp, 0, sizeof(*mp));
+	__asm__ __volatile__("":::"memory");
+
+	/* mark as unused */
+	mp->sc->arpcom.ac_if.if_init = 0;
+
+	return 0;
+}
+
+/*
+ * Enqueue a mbuf chain or a raw data buffer for transmission;
+ * RETURN: #bytes sent or -1 if there are not enough free descriptors
+ *
+ * If 'len' is <=0 then 'm_head' is assumed to point to a mbuf chain.
+ * OTOH, a raw data packet (or a different type of buffer)
+ * may be sent (non-BSD driver) by pointing data_p to the start of
+ * the data and passing 'len' > 0.
+ * 'm_head' is passed back to the 'cleanup_txbuf()' callback.
+ *
+ * Comments: software cache-flushing incurs a penalty if the
+ *           packet cannot be queued since it is flushed anyways.
+ *           The algorithm is slightly more efficient in the normal
+ *			 case, though.
+ *
+ * RETURNS: # bytes enqueued to device for transmission or -1 if no
+ *          space in the TX ring was available.
+ */
+
+#if 0
+#define NEXT_TXD(mp, bd) ((bd_rdfl( bd ) & TSEC_TXBD_W) ? mp->tx_ring : (bd + 1))
+#endif
+
+/*
+ * allocate a new cluster and copy an existing chain there;
+ * old chain is released...
+ */
+static struct mbuf *
+repackage_chain(struct mbuf *m_head)
+{
+struct mbuf *m;
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+
+	if ( !m ) {
+		goto bail;
+	}
+
+	MCLGET(m, M_DONTWAIT);
+
+	if ( !(M_EXT & m->m_flags) ) {
+		m_freem(m);
+		m = 0;
+		goto bail;
+	}
+
+	m_copydata(m_head, 0, MCLBYTES, mtod(m, caddr_t));
+	m->m_pkthdr.len = m->m_len = m_head->m_pkthdr.len;
+
+bail:
+	m_freem(m_head);
+	return m;
+}
+
+static inline unsigned
+tsec_assign_desc( TSEC_BD *bd, uint32_t buf, unsigned len, uint32_t flags)
+{
+	st_be16 ( &bd->len, (uint16_t)len );
+	bd_wrbuf( bd, buf );
+	bd_cslfl( bd, flags, TSEC_TXBD_R | TSEC_TXBD_L );
+	return len;
+}
+
+
+int
+BSP_tsec_send_buf(struct tsec_private *mp, void *m_head, void *data_p, int len)
+{
+int						rval;
+register TSEC_BD		*bd;
+register unsigned       l,d,t;
+register struct mbuf	*m1;
+int						nmbs;
+int						ismbuf = (len <= 0);
+
+#if DEBUG > 2
+	printf("send entering...\n");
+	tsec_dump_tring(mp);
+#endif
+/* Only way to get here is when we discover that the mbuf chain
+ * is too long for the tx ring
+ */
+startover:
+
+	rval = 0;
+
+#ifdef PARANOIA
+	assert(m_head);
+#endif
+
+	/* if no descriptor is available; try to wipe the queue */
+	if ( (mp->tx_avail < 1) && TSEC_CLEAN_ON_SEND(mp)<=0 ) {
+		return -1;
+	}
+
+	t = mp->tx_tail;
+
+#ifdef PARANOIA
+	assert( ! bd_rdbuf( &mp->tx_ring[t] ) );
+	assert( ! mp->tx_ring_user[t]         );
+#endif
+
+	if ( ! (m1 = m_head) )
+		return 0;
+
+	if ( ismbuf ) {
+		/* find first mbuf with actual data */
+		while ( 0 == m1->m_len ) {
+			if ( ! (m1 = m1->m_next) ) {
+				/* end reached and still no data to send ?? */
+				m_freem(m_head);
+				return 0;
+			}
+		}
+	}
+
+	/* Don't use the first descriptor yet because BSP_tsec_swipe_tx()
+	 * needs bd->buf == NULL as a marker. Hence, we
+	 * start with the second mbuf and fill the first descriptor
+	 * last.
+	 */
+
+	l = t;
+	d = NEXT_TXI(mp,t);
+
+	mp->tx_avail--;
+
+	nmbs = 1;
+	if ( ismbuf ) {
+			register struct mbuf *m;
+			for ( m=m1->m_next; m; m=m->m_next ) {
+					if ( 0 == m->m_len )
+							continue;	/* skip empty mbufs */
+
+					nmbs++;
+
+					if ( mp->tx_avail < 1 && TSEC_CLEAN_ON_SEND(mp)<=0 ) {
+							/* not enough descriptors; cleanup...
+							 * the first slot was never used, so we start
+							 * at mp->d_tx_h->next;
+							 */
+							for ( l = NEXT_TXI(mp, t); l!=d; l=NEXT_TXI(mp, l) ) {
+									bd = & mp->tx_ring[l];
+#ifdef PARANOIA
+									assert( mp->tx_ring_user[l] == 0 );
+#endif
+									bd_wrbuf( bd, 0 );
+									bd_clrfl( bd, TSEC_TXBD_R | TSEC_TXBD_L );
+
+									mp->tx_avail++;
+							}
+							mp->tx_avail++;
+							if ( nmbs > TX_AVAILABLE_RING_SIZE(mp) ) {
+									/* this chain will never fit into the ring */
+									if ( nmbs > mp->stats.maxchain )
+											mp->stats.maxchain = nmbs;
+									mp->stats.repack++;
+									if ( ! (m_head = repackage_chain(m_head)) ) {
+											/* no cluster available */
+											mp->stats.odrops++;
+#ifdef PARANOIA
+											printf("send return 0\n");
+											tsec_dump_tring(mp);
+#endif
+											return 0;
+									}
+#ifdef PARANOIA
+									printf("repackaged; start over\n");
+#endif
+									goto startover;
+							}
+#ifdef PARANOIA
+							printf("send return -1\n");
+							tsec_dump_tring(mp);
+#endif
+							return -1;
+					}
+
+					mp->tx_avail--;
+
+#ifdef PARANOIA
+					assert( ! mp->tx_ring_user[d]         );
+					if ( d == t ) {
+						tsec_dump_tring(mp);
+						printf("l %i, d %i, t %i, nmbs %i\n", l,d,t, nmbs);
+					} else
+					assert( d != t                        );
+					assert( ! bd_rdbuf( &mp->tx_ring[d] ) );
+#endif
+
+					/* fill this slot */
+					rval += tsec_assign_desc( &mp->tx_ring[d], mtod(m, uint32_t), m->m_len, TSEC_TXBD_R);
+
+					FLUSH_BUF(mtod(m, uint32_t), m->m_len);
+
+					l = d;
+					d = NEXT_TXI(mp, d);
+			}
+
+		/* fill first slot - don't release to DMA yet */
+		rval += tsec_assign_desc( &mp->tx_ring[t], mtod(m1, uint32_t), m1->m_len, 0);
+
+
+		FLUSH_BUF(mtod(m1, uint32_t), m1->m_len);
+
+	} else {
+		/* fill first slot with raw buffer - don't release to DMA yet */
+		rval += tsec_assign_desc( &mp->tx_ring[t], (uint32_t)data_p, len, 0);
+
+		FLUSH_BUF( (uint32_t)data_p, len);
+	}
+
+	/* tag last slot; this covers the case where 1st==last */
+	bd_setfl( &mp->tx_ring[l], TSEC_TXBD_L );
+
+	/* mbuf goes into last desc */
+	mp->tx_ring_user[l] = m_head;
+
+	membarrier();
+
+	/* turn over the whole chain by flipping ownership of the first desc */
+	bd_setfl( &mp->tx_ring[t], TSEC_TXBD_R );
+
+	membarrier();
+
+#if DEBUG > 2
+	printf("send return (l=%i, t=%i, d=%i) %i\n", l, t, d, rval);
+	tsec_dump_tring(mp);
+#endif
+
+	/* notify the device */
+	fec_wr( mp->base, TSEC_TSTAT, TSEC_TSTAT_THLT );
+
+	/* Update softc */
+	mp->stats.packet++;
+	if ( nmbs > mp->stats.maxchain )
+		mp->stats.maxchain = nmbs;
+
+	/* remember new tail */
+	mp->tx_tail = d;
+
+	return rval; /* #bytes sent */
+}
+
+/*
+ * Retrieve all received buffers from the RX ring, replacing them
+ * by fresh ones (obtained from the alloc_rxbuf() callback). The
+ * received buffers are passed to consume_rxbuf().
+ *
+ * RETURNS: number of buffers processed.
+ */
+int
+BSP_tsec_swipe_rx(struct tsec_private *mp)
+{
+int						rval = 0, err;
+unsigned                i;
+uint16_t				flags;
+TSEC_BD                 *bd;
+void					*newbuf;
+int						sz;
+uint16_t                len;
+uintptr_t               baddr;
+
+	i     = mp->rx_tail;
+	bd    = mp->rx_ring + i;
+	flags = bd_rdfl( bd );
+
+	while ( ! (TSEC_RXBD_E & flags) ) {
+
+		/* err is not valid if not qualified by TSEC_RXBD_L */
+		if ( ( err = (TSEC_RXBD_ERROR & flags) ) ) {
+			/* make sure error is < 0 */
+			err |= 0xffff0000;
+			/* pass 'L' flag out so they can verify... */
+			err |= (flags & TSEC_RXBD_L);
+		}
+
+#ifdef PARANOIA
+		assert( flags & TSEC_RXBD_L );
+		assert( mp->rx_ring_user[i] );
+#endif
+
+		if ( err || !(newbuf = mp->alloc_rxbuf(&sz, &baddr)) ) {
+			/* drop packet and recycle buffer */
+			newbuf = mp->rx_ring_user[i];
+			mp->consume_rxbuf( 0, mp->consume_rxbuf_arg, err );
+		} else {
+			len = ld_be16( &bd->len );
+
+#ifdef PARANOIA
+			assert( 0 == (baddr & (RX_BUF_ALIGNMENT-1)) );
+			assert( len > 0 );
+#endif
+
+			mp->consume_rxbuf( mp->rx_ring_user[i], mp->consume_rxbuf_arg, len );
+
+			mp->rx_ring_user[i] = newbuf;
+			st_be16( &bd->len, sz );
+			bd_wrbuf( bd, baddr );
+		}
+
+		RTEMS_COMPILER_MEMORY_BARRIER();
+
+		bd_wrfl( &mp->rx_ring[i], (flags & ~TSEC_RXBD_ERROR) | TSEC_RXBD_E );
+
+		rval++;
+
+		i  = NEXT_RXI( mp, i );
+		bd = mp->rx_ring + i;
+		flags = bd_rdfl( bd );
+	}
+
+	fec_wr( mp->base, TSEC_RSTAT, TSEC_RSTAT_QHLT );
+
+	mp->rx_tail = i;
+	return rval;
+}
+
+/* read ethernet address from hw to buffer */
+void
+BSP_tsec_read_eaddr(struct tsec_private *mp, unsigned char *eaddr)
+{
+union {
+	uint32_t u;
+	uint16_t s[2];
+	uint8_t  c[4];
+} x;
+	st_le32( (volatile uint32_t *)(eaddr+2), fec_rd(mp->base, TSEC_MACSTNADDR1) );
+	x.u = fec_rd(mp->base, TSEC_MACSTNADDR2);
+	st_le16( (volatile uint16_t *)(eaddr), x.s[0]);
+}
+
+/* mdio / mii interface wrappers for rtems_mii_ioctl API */
+
+/*
+ * Busy-wait -- this can take a while: I measured ~550 timebase-ticks
+ * @333333333Hz, TB divisor is 8 -> 13us
+ */
+static inline void mii_wait(FEC_Enet_Base b)
+{
+	while ( (TSEC_MIIMIND_BUSY & fec_rd( b, TSEC_MIIMIND )) )
+		;
+}
+
+/* MII operations are rather slow :-( */
+static int
+tsec_mdio_rd(int phyidx, void *uarg, unsigned reg, uint32_t *pval)
+{
+uint32_t             v;
+#ifdef TEST_MII_TIMING
+uint32_t             t;
+#endif
+struct tsec_private *mp = uarg;
+FEC_Enet_Base        b  = mp->phy_base;
+
+	if ( phyidx != 0 )
+		return -1; /* only one phy supported for now */
+
+	/* write phy and register address */
+	fec_wr( b, TSEC_MIIMADD, TSEC_MIIMADD_ADDR(mp->phy,reg) );
+
+	/* clear READ bit */
+	v = fec_rd( b, TSEC_MIIMCOM );
+	fec_wr( b, TSEC_MIIMCOM, v & ~TSEC_MIIMCOM_READ );
+
+#ifdef TEST_MII_TIMING
+	t = tb_rd();
+#endif
+
+	/* trigger READ operation by READ-bit 0-->1 transition */
+	fec_wr( b, TSEC_MIIMCOM, v |  TSEC_MIIMCOM_READ );
+
+	/* (busy) wait for completion */
+
+	mii_wait( b );
+
+	/* Ugly workaround: I observed that the link status
+	 * is not correctly reported when the link changes to
+	 * a good status - a failed link is reported until
+	 * we read twice :-(
+	 */
+	if ( MII_BMSR == reg ) {
+		/* trigger a second read operation */
+		fec_wr( b, TSEC_MIIMCOM, v & ~TSEC_MIIMCOM_READ );
+		fec_wr( b, TSEC_MIIMCOM, v |  TSEC_MIIMCOM_READ );
+		mii_wait( b );
+	}
+
+#ifdef TEST_MII_TIMING
+	t = tb_rd() - t;
+	printf("Reading MII took %"PRIi32"\n", t);
+#endif
+	/* return result */
+	*pval = fec_rd( b, TSEC_MIIMSTAT ) & 0xffff;
+	return 0;
+}
+
+STATIC int
+tsec_mdio_wr(int phyidx, void *uarg, unsigned reg, uint32_t val)
+{
+#ifdef TEST_MII_TIMING
+uint32_t             t;
+#endif
+struct tsec_private *mp = uarg;
+FEC_Enet_Base        b  = mp->phy_base;
+
+	if ( phyidx != 0 )
+		return -1; /* only one phy supported for now */
+
+#ifdef TEST_MII_TIMING
+	t = tb_rd();
+#endif
+
+	fec_wr( b, TSEC_MIIMADD, TSEC_MIIMADD_ADDR(mp->phy,reg) );
+	fec_wr( b, TSEC_MIIMCON, val & 0xffff );
+
+	mii_wait( b );
+
+#ifdef TEST_MII_TIMING
+	t = tb_rd() - t;
+	printf("Writing MII took %"PRIi32"\n", t);
+#endif
+
+	return 0;
+}
+
+/* Public, locked versions */
+uint32_t
+BSP_tsec_mdio_rd(struct tsec_private *mp, unsigned reg)
+{
+uint32_t val, rval;
+
+	REGLOCK();
+	rval = tsec_mdio_rd(0, mp, reg, &val ) ? -1 : val;
+	REGUNLOCK();
+
+	return rval;
+}
+
+int
+BSP_tsec_mdio_wr(struct tsec_private *mp, unsigned reg, uint32_t val)
+{
+int rval;
+
+	REGLOCK();
+	rval = tsec_mdio_wr(0, mp, reg, val);
+	REGUNLOCK();
+
+	return rval;
+}
+
+static struct rtems_mdio_info tsec_mdio = {
+	mdio_r:	  tsec_mdio_rd,
+	mdio_w:	  tsec_mdio_wr,
+	has_gmii: 1,
+};
+
+
+/*
+ * read/write media word.
+ *   'cmd': can be SIOCGIFMEDIA, SIOCSIFMEDIA, 0 or 1. The latter
+ *          are aliased to the former for convenience.
+ *  'parg': pointer to media word.
+ *
+ * RETURNS: 0 on success, nonzero on error
+ */
+int
+BSP_tsec_media_ioctl(struct tsec_private *mp, int cmd, int *parg)
+{
+int rval;
+	/* alias cmd == 0,1 for convenience when calling from shell */
+	switch ( cmd ) {
+		case 0: cmd = SIOCGIFMEDIA;
+			break;
+		case 1: cmd = SIOCSIFMEDIA;
+		case SIOCGIFMEDIA:
+		case SIOCSIFMEDIA:
+			break;
+		default: return -1;
+	}
+	REGLOCK();
+	tsec_mdio.has_gmii = mp->isfec ? 0 : 1;
+	rval = rtems_mii_ioctl(&tsec_mdio, mp, cmd, parg);
+	REGUNLOCK();
+	return rval;
+}
+
+/* Interrupt related routines */
+
+/*
+ * When it comes to interrupts the chip has two rather
+ * annoying features:
+ *   1 once an IRQ is pending, clearing the IMASK does not
+ *     de-assert the interrupt line.
+ *   2 the chip has three physical interrupt lines even though
+ *     all events are reported in a single register. Rather
+ *     useless; we must hook 3 ISRs w/o any real benefit.
+ *     In fact, it makes our life a bit more difficult:
+ *
+ * Hence, for (1) we would have to mask interrupts at the PIC
+ * but to re-enable them we would have to do that three times
+ * because of (2).
+ *
+ * Therefore, we take the following approach:
+ *
+ *   ISR masks interrupts on the TSEC, acks/clears them
+ *   and stores the acked irqs in the device struct where
+ *   it is picked up by BSP_tsec_ack_irq_mask().
+ *
+ */
+
+static inline uint32_t
+tsec_dis_irqs(struct tsec_private *mp, uint32_t mask)
+{
+uint32_t rval;
+
+	rval = mp->irq_mask_cache;
+	if ( (TSEC_LINK_INTR & mask & mp->irq_mask_cache) )
+		phy_dis_irq( mp );
+	mp->irq_mask_cache = rval & ~mask;
+	fec_wr( mp->base, TSEC_IMASK, (mp->irq_mask_cache & ~TSEC_LINK_INTR) );
+
+	return rval;
+}
+
+static inline uint32_t
+tsec_dis_clr_irqs(struct tsec_private *mp)
+{
+uint32_t      rval;
+FEC_Enet_Base b = mp->base;
+
+	rval  = fec_rd( b, TSEC_IEVENT);
+
+	/* Make sure we mask out the link intr */
+	rval &= ~TSEC_LINK_INTR;
+
+	tsec_dis_irqs( mp, rval );
+	fec_wr( b, TSEC_IEVENT, rval );
+
+	return rval;
+}
+
+/*
+ * We have 3 different ISRs just so we can count
+ * interrupt types independently...
+ */
+
+static void tsec_xisr(rtems_irq_hdl_param arg)
+{
+struct tsec_private   *mp = (struct tsec_private *)arg;
+rtems_interrupt_level l;
+
+	rtems_interrupt_disable( l );
+		mp->irq_pending |= tsec_dis_clr_irqs( mp );
+	rtems_interrupt_enable( l );
+
+	mp->stats.xirqs++;
+
+	if ( mp->isr )
+		mp->isr( mp->isr_arg );
+	else
+		rtems_bsdnet_event_send( mp->tid, mp->event );
+}
+
+static void tsec_risr(rtems_irq_hdl_param arg)
+{
+struct tsec_private   *mp = (struct tsec_private *)arg;
+rtems_interrupt_level l;
+
+	rtems_interrupt_disable( l );
+		mp->irq_pending |= tsec_dis_clr_irqs( mp );
+	rtems_interrupt_enable( l );
+
+	mp->stats.rirqs++;
+
+	if ( mp->isr )
+		mp->isr( mp->isr_arg );
+	else
+		rtems_bsdnet_event_send( mp->tid, mp->event );
+}
+
+static void tsec_eisr(rtems_irq_hdl_param arg)
+{
+struct tsec_private   *mp = (struct tsec_private *)arg;
+rtems_interrupt_level l;
+uint32_t              pending;
+
+	rtems_interrupt_disable( l );
+		/* make local copy since ISR may ack and clear mp->pending;
+		 * also, we want the fresh bits not the ORed state including
+		 * the past...
+		 */
+		mp->irq_pending |= (pending = tsec_dis_clr_irqs( mp ));
+	rtems_interrupt_enable( l );
+
+	mp->stats.eirqs++;
+
+	if ( mp->isr )
+		mp->isr( mp->isr_arg );
+	else
+		rtems_bsdnet_event_send( mp->tid, mp->event );
+
+	if ( (TSEC_IEVENT_TXE & pending) ) {
+		if ( (TSEC_IEVENT_EBERR & pending) && ++mp->stats.eberrs > MAXEBERRS ) {
+			printk(DRVNAME" BAD error: max # of DMA bus errors reached\n");
+		} else {
+			/* Restart DMA -- do we have to clear DMACTRL[GTS], too ?? */
+			fec_wr( mp->base, TSEC_TSTAT, TSEC_TSTAT_THLT );
+			mp->stats.dmarst++;
+		}
+	}
+}
+
+static void tsec_lisr(rtems_irq_hdl_param arg)
+{
+struct tsec_private   *mp = (struct tsec_private *)arg;
+rtems_interrupt_level l;
+
+	if ( phy_irq_pending( mp ) ) {
+
+		rtems_interrupt_disable( l );
+			tsec_dis_irqs( mp, TSEC_LINK_INTR );
+			mp->irq_pending |= TSEC_LINK_INTR;
+		rtems_interrupt_enable( l );
+
+		mp->stats.lirqs++;
+
+		if ( mp->isr )
+			mp->isr( mp->isr_arg );
+		else
+			rtems_bsdnet_event_send( mp->tid, mp->event );
+	}
+}
+
+/* Enable interrupts at device */
+void
+BSP_tsec_enable_irq_mask(struct tsec_private *mp, uint32_t mask)
+{
+rtems_interrupt_level l;
+
+	mask &= mp->irq_mask;
+
+	rtems_interrupt_disable( l );
+	if ( (TSEC_LINK_INTR & mask) && ! (TSEC_LINK_INTR & mp->irq_mask_cache) )
+		phy_en_irq( mp );
+	mp->irq_mask_cache |= mask;
+	fec_wr( mp->base, TSEC_IMASK, (mp->irq_mask_cache & ~TSEC_LINK_INTR) );
+	rtems_interrupt_enable( l );
+}
+
+void
+BSP_tsec_enable_irqs(struct tsec_private *mp)
+{
+	BSP_tsec_enable_irq_mask(mp, -1);
+}
+
+/* Disable interrupts at device */
+uint32_t
+BSP_tsec_disable_irq_mask(struct tsec_private *mp, uint32_t mask)
+{
+uint32_t              rval;
+rtems_interrupt_level l;
+
+	rtems_interrupt_disable( l );
+		rval = tsec_dis_irqs(mp, mask);
+	rtems_interrupt_enable( l );
+
+	return rval;
+}
+
+void
+BSP_tsec_disable_irqs(struct tsec_private *mp)
+{
+rtems_interrupt_level l;
+
+	rtems_interrupt_disable( l );
+		tsec_dis_irqs(mp, -1);
+	rtems_interrupt_enable( l );
+}
+
+/*
+ * Acknowledge (and clear) interrupts.
+ * RETURNS: interrupts that were raised.
+ */
+uint32_t
+BSP_tsec_ack_irq_mask(struct tsec_private *mp, uint32_t mask)
+{
+uint32_t              rval;
+rtems_interrupt_level l;
+
+	rtems_interrupt_disable( l );
+		rval = mp->irq_pending;
+		mp->irq_pending &= ~ mask;
+	rtems_interrupt_enable( l );
+
+	if ( (rval & TSEC_LINK_INTR & mask) ) {
+		/* interacting with the PHY is slow so
+		 * we do it only if we have to...
+		 */
+		phy_ack_irq( mp );
+	}
+
+	return rval & mp->irq_mask;
+}
+
+uint32_t
+BSP_tsec_ack_irqs(struct tsec_private *mp)
+{
+	return BSP_tsec_ack_irq_mask(mp, -1);
+}
+
+/* Retrieve the driver daemon TID that was passed to
+ * BSP_tsec_setup().
+ */
+
+rtems_id
+BSP_tsec_get_tid(struct tsec_private *mp)
+{
+	return mp->tid;
+}
+
+struct tsec_private *
+BSP_tsec_getp(unsigned index)
+{
+	if ( index >= TSEC_NUM_DRIVER_SLOTS )
+		return 0;
+	return & theTsecEths[index].pvt;
+}
+
+/*
+ *
+ * Example driver task loop (note: no synchronization of
+ * buffer access shown!).
+ * RTEMS_EVENTx = 0,1 or 2 depending on IF unit.
+ *
+ *    / * setup (obtain handle) and initialize hw here * /
+ *
+ *    do {
+ *      / * ISR disables IRQs and posts event * /
+ *		rtems_event_receive( RTEMS_EVENTx, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &evs );
+ *		irqs = BSP_tsec_ack_irqs(handle);
+ *      if ( irqs & BSP_TSEC_IRQ_TX ) {
+ *			BSP_tsec_swipe_tx(handle); / * cleanup_txbuf() callback executed * /
+ *		}
+ *      if ( irqs & BSP_TSEC_IRQ_RX ) {
+ *			BSP_tsec_swipe_rx(handle); / * alloc_rxbuf() and consume_rxbuf() executed * /
+ *		}
+ *		BSP_tsec_enable_irq_mask(handle, -1);
+ *    } while (1);
+ *
+ */
+
+/* BSDNET SUPPORT/GLUE ROUTINES */
+
+STATIC void
+tsec_stop(struct tsec_softc *sc)
+{
+	BSP_tsec_stop_hw(&sc->pvt);
+	sc->arpcom.ac_if.if_timer = 0;
+}
+
+/* allocate a mbuf for RX with a properly aligned data buffer
+ * RETURNS 0 if allocation fails
+ */
+static void *
+alloc_mbuf_rx(int *psz, uintptr_t *paddr)
+{
+struct mbuf		*m;
+unsigned long	l,o;
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if ( !m )
+		return 0;
+	MCLGET(m, M_DONTWAIT);
+	if ( ! (m->m_flags & M_EXT) ) {
+		m_freem(m);
+		return 0;
+	}
+
+	o = mtod(m, unsigned long);
+	l = ALIGNTO(o, RX_BUF_ALIGNMENT) - o;
+
+	/* align start of buffer */
+	m->m_data += l;
+
+	/* reduced length */
+	l = MCLBYTES - l;
+
+	m->m_len   = m->m_pkthdr.len = l;
+	*psz       = m->m_len;
+	*paddr     = mtod(m, unsigned long);
+
+	return (void*) m;
+}
+
+static void consume_rx_mbuf(void *buf, void *arg, int len)
+{
+struct ifnet *ifp = arg;
+struct mbuf    *m = buf;
+
+	if ( len <= 0 ) {
+		ifp->if_iqdrops++;
+		if ( len < 0 ) {
+			ifp->if_ierrors++;
+		}
+		if ( m )
+			m_freem(m);
+	} else {
+		struct ether_header *eh;
+
+			eh			= (struct ether_header *)(mtod(m, unsigned long) + ETH_RX_OFFSET);
+			m->m_len	= m->m_pkthdr.len = len - sizeof(struct ether_header) - ETH_RX_OFFSET - ETH_CRC_LEN;
+			m->m_data  += sizeof(struct ether_header) + ETH_RX_OFFSET;
+			m->m_pkthdr.rcvif = ifp;
+
+			ifp->if_ipackets++;
+			ifp->if_ibytes  += m->m_pkthdr.len;
+
+			/* send buffer upwards */
+			if (0) {
+				/* Low-level debugging */
+				int i;
+				for (i=0; i<13; i++) {
+					printf("%02X:",((char*)eh)[i]);
+				}
+				printf("%02X\n",((char*)eh)[i]);
+				for (i=0; i<m->m_len; i++) {
+					if ( !(i&15) )
+						printf("\n");
+					printf("0x%02x ",mtod(m,char*)[i]);
+				}
+				printf("\n");
+			}
+
+			if (0) /* Low-level debugging/testing without bsd stack */
+				m_freem(m);
+			else
+				ether_input(ifp, eh, m);
+	}
+}
+
+static void release_tx_mbuf(void *buf, void *arg, int err)
+{
+struct ifnet *ifp = arg;
+struct mbuf  *mb  = buf;
+
+	if ( err ) {
+		ifp->if_oerrors++;
+	} else {
+		ifp->if_opackets++;
+	}
+	ifp->if_obytes += mb->m_pkthdr.len;
+	m_freem(mb);
+}
+
+/* BSDNET DRIVER CALLBACKS */
+
+static void
+tsec_init(void *arg)
+{
+struct tsec_softc	*sc  = arg;
+struct ifnet		*ifp = &sc->arpcom.ac_if;
+int                 media;
+
+	BSP_tsec_init_hw(&sc->pvt, ifp->if_flags & IFF_PROMISC, sc->arpcom.ac_enaddr);
+
+	/* Determine initial link status and block sender if there is no link */
+	media = IFM_MAKEWORD(0, 0, 0, 0);
+	if ( 0 == BSP_tsec_media_ioctl(&sc->pvt, SIOCGIFMEDIA, &media) ) {
+		if ( (IFM_LINK_OK & media) ) {
+			ifp->if_flags &= ~IFF_OACTIVE;
+		} else {
+			ifp->if_flags |=  IFF_OACTIVE;
+		}
+	}
+
+	tsec_update_mcast(ifp);
+	ifp->if_flags |= IFF_RUNNING;
+	sc->arpcom.ac_if.if_timer = 0;
+}
+
+/* bsdnet driver entry to start transmission */
+static void
+tsec_start(struct ifnet *ifp)
+{
+struct tsec_softc	*sc = ifp->if_softc;
+struct mbuf			*m  = 0;
+
+	while ( ifp->if_snd.ifq_head ) {
+		IF_DEQUEUE( &ifp->if_snd, m );
+		if ( BSP_tsec_send_buf(&sc->pvt, m, 0, 0) < 0 ) {
+			IF_PREPEND( &ifp->if_snd, m);
+			ifp->if_flags |= IFF_OACTIVE;
+			break;
+		}
+		/* need to do this really only once
+		 * but it's cheaper this way.
+		 */
+		ifp->if_timer = 2*IFNET_SLOWHZ;
+	}
+}
+
+/* bsdnet driver entry; */
+static void
+tsec_watchdog(struct ifnet *ifp)
+{
+struct tsec_softc	*sc = ifp->if_softc;
+
+	ifp->if_oerrors++;
+	printk(DRVNAME"%i: watchdog timeout; resetting\n", ifp->if_unit);
+
+	tsec_init(sc);
+	tsec_start(ifp);
+}
+
+static void
+tsec_update_mcast(struct ifnet *ifp)
+{
+struct tsec_softc *sc = ifp->if_softc;
+struct ether_multi     *enm;
+struct ether_multistep step;
+
+	if ( IFF_ALLMULTI & ifp->if_flags ) {
+		BSP_tsec_mcast_filter_accept_all( &sc->pvt );
+	} else {
+		BSP_tsec_mcast_filter_clear( &sc->pvt );
+
+		ETHER_FIRST_MULTI(step, (struct arpcom *)ifp, enm);
+
+		while ( enm ) {
+			if ( memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) )
+				assert( !"Should never get here; IFF_ALLMULTI should be set!" );
+
+			BSP_tsec_mcast_filter_accept_add(&sc->pvt, enm->enm_addrlo);
+
+			ETHER_NEXT_MULTI(step, enm);
+		}
+	}
+}
+
+/* bsdnet driver ioctl entry */
+static int
+tsec_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
+{
+struct tsec_softc	*sc   = ifp->if_softc;
+struct ifreq		*ifr  = (struct ifreq *)data;
+#if 0
+uint32_t			v;
+#endif
+int					error = 0;
+int					f;
+
+	switch ( cmd ) {
+  		case SIOCSIFFLAGS:
+			f = ifp->if_flags;
+			if ( f & IFF_UP ) {
+				if ( ! ( f & IFF_RUNNING ) ) {
+					tsec_init(sc);
+				} else {
+					if ( (f & IFF_PROMISC) != (sc->bsd.oif_flags & IFF_PROMISC) ) {
+						/* Hmm - the manual says we must change the RCTRL
+						 * register only after a reset or if DMACTRL[GRS]
+						 * is cleared which is the normal operating
+						 * condition. I hope this is legal ??
+						 */
+						if ( (f & IFF_PROMISC) ) {
+							fec_set( sc->pvt.base, TSEC_RCTRL, TSEC_RCTRL_PROM );
+						} else {
+							fec_clr( sc->pvt.base, TSEC_RCTRL, TSEC_RCTRL_PROM );
+						}
+					}
+					/* FIXME: other flag changes are ignored/unimplemented */
+				}
+			} else {
+				if ( f & IFF_RUNNING ) {
+					tsec_stop(sc);
+					ifp->if_flags  &= ~(IFF_RUNNING | IFF_OACTIVE);
+				}
+			}
+			sc->bsd.oif_flags = ifp->if_flags;
+		break;
+
+  		case SIOCGIFMEDIA:
+  		case SIOCSIFMEDIA:
+			error = BSP_tsec_media_ioctl(&sc->pvt, cmd, &ifr->ifr_media);
+		break;
+
+ 		case SIOCADDMULTI:
+ 		case SIOCDELMULTI:
+			error = (cmd == SIOCADDMULTI)
+		    		? ether_addmulti(ifr, &sc->arpcom)
+				    : ether_delmulti(ifr, &sc->arpcom);
+
+			if (error == ENETRESET) {
+				if (ifp->if_flags & IFF_RUNNING) {
+					tsec_update_mcast(ifp);
+				}
+				error = 0;
+			}
+		break;
+
+ 		case SIO_RTEMS_SHOW_STATS:
+			BSP_tsec_dump_stats( &sc->pvt, stdout );
+		break;
+
+		default:
+			error = ether_ioctl(ifp, cmd, data);
+		break;
+	}
+
+	return error;
+}
+
+/* DRIVER TASK */
+
+/* Daemon task does all the 'interrupt' work */
+static void tsec_daemon(void *arg)
+{
+struct tsec_softc	*sc;
+struct ifnet		*ifp;
+rtems_event_set		evs;
+	for (;;) {
+		rtems_bsdnet_event_receive( EV_MSK, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &evs );
+		evs &= EV_MSK;
+		for ( sc = theTsecEths; evs; evs>>=EV_PER_UNIT, sc++ ) {
+			if ( EV_IS_ANY(evs) ) {
+
+				register uint32_t x;
+
+				ifp = &sc->arpcom.ac_if;
+
+				if ( !(ifp->if_flags & IFF_UP) ) {
+					tsec_stop(sc);
+					ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
+					continue;
+				}
+
+				if ( !(ifp->if_flags & IFF_RUNNING) ) {
+					/* event could have been pending at the time hw was stopped;
+					 * just ignore...
+					 */
+					continue;
+				}
+
+				x = BSP_tsec_ack_irqs(&sc->pvt);
+
+				if ( TSEC_LINK_INTR & x ) {
+					/* phy status changed */
+					int media;
+
+#ifdef DEBUG
+					printf("LINK INTR\n");
+#endif
+					if ( -1 != (media = mac_set_duplex( &sc->pvt )) ) {
+#ifdef DEBUG
+						rtems_ifmedia2str( media, 0, 0 );
+						printf("\n");
+#endif
+						if ( IFM_LINK_OK & media ) {
+							ifp->if_flags &= ~IFF_OACTIVE;
+							tsec_start(ifp);
+						} else {
+							/* stop sending */
+							ifp->if_flags |= IFF_OACTIVE;
+						}
+					}
+				}
+
+				/* free tx chain */
+				if ( (TSEC_TXIRQ & x) && BSP_tsec_swipe_tx(&sc->pvt) ) {
+					ifp->if_flags &= ~IFF_OACTIVE;
+					if ( TX_AVAILABLE_RING_SIZE(&sc->pvt) == sc->pvt.tx_avail )
+						ifp->if_timer = 0;
+					tsec_start(ifp);
+				}
+				if ( (TSEC_RXIRQ & x) )
+					BSP_tsec_swipe_rx(&sc->pvt);
+
+				BSP_tsec_enable_irq_mask(&sc->pvt, -1);
+			}
+		}
+	}
+}
+
+/* PUBLIC RTEMS BSDNET ATTACH FUNCTION */
+int
+rtems_tsec_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching)
+{
+char				*unitName;
+int					unit,i,cfgUnits;
+struct	tsec_softc *sc;
+struct	ifnet		*ifp;
+
+	unit = rtems_bsdnet_parse_driver_name(ifcfg, &unitName);
+	if ( unit <= 0 || unit > TSEC_NUM_DRIVER_SLOTS ) {
+		printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, TSEC_NUM_DRIVER_SLOTS);
+		return 1;
+	}
+
+	sc         = &theTsecEths[unit-1];
+	ifp        = &sc->arpcom.ac_if;
+
+	if ( attaching ) {
+		if ( ifp->if_init ) {
+			printk(DRVNAME": instance %i already attached.\n", unit);
+			return -1;
+		}
+
+		for ( i=cfgUnits = 0; i<TSEC_NUM_DRIVER_SLOTS; i++ ) {
+			if ( theTsecEths[i].arpcom.ac_if.if_init )
+				cfgUnits++;
+		}
+		cfgUnits++; /* this new one */
+
+		/* lazy init of TID should still be thread-safe because we are protected
+		 * by the global networking semaphore..
+		 */
+		if ( !tsec_tid ) {
+			/* newproc uses the 1st 4 chars of name string to build an rtems name */
+			tsec_tid = rtems_bsdnet_newproc("FECd", 4096, tsec_daemon, 0);
+		}
+
+		if ( !BSP_tsec_setup( unit,
+						     tsec_tid,
+						     release_tx_mbuf, ifp,
+						     alloc_mbuf_rx,
+						     consume_rx_mbuf, ifp,
+						     ifcfg->rbuf_count,
+						     ifcfg->xbuf_count,
+			                 TSEC_RXIRQ | TSEC_TXIRQ | TSEC_LINK_INTR) ) {
+			return -1;
+		}
+
+		if ( nmbclusters < sc->pvt.rx_ring_size * cfgUnits + 60 /* arbitrary */ )  {
+			printk(DRVNAME"%i: (tsec ethernet) Your application has not enough mbuf clusters\n", unit);
+			printk(     "                      configured for this driver.\n");
+			return -1;
+		}
+
+		if ( ifcfg->hardware_address ) {
+			memcpy(sc->arpcom.ac_enaddr, ifcfg->hardware_address, ETHER_ADDR_LEN);
+		} else {
+			/* read back from hardware assuming that MotLoad already had set it up */
+			BSP_tsec_read_eaddr(&sc->pvt, sc->arpcom.ac_enaddr);
+		}
+
+		ifp->if_softc			= sc;
+		ifp->if_unit			= unit;
+		ifp->if_name			= unitName;
+
+		ifp->if_mtu				= ifcfg->mtu ? ifcfg->mtu : ETHERMTU;
+
+		ifp->if_init			= tsec_init;
+		ifp->if_ioctl			= tsec_ioctl;
+		ifp->if_start			= tsec_start;
+		ifp->if_output			= ether_output;
+		/*
+		 * While nonzero, the 'if->if_timer' is decremented
+		 * (by the networking code) at a rate of IFNET_SLOWHZ (1hz) and 'if_watchdog'
+		 * is called when it expires.
+		 * If either of those fields is 0 the feature is disabled.
+		 */
+		ifp->if_watchdog		= tsec_watchdog;
+		ifp->if_timer			= 0;
+
+		sc->bsd.oif_flags		= /* ... */
+		ifp->if_flags			= IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
+
+		/*
+		 * if unset, this set to 10Mbps by ether_ifattach; seems to be unused by bsdnet stack;
+		 * could be updated along with phy speed, though...
+		ifp->if_baudrate		= 10000000;
+		*/
+
+		/* NOTE: ether_output drops packets if ifq_len >= ifq_maxlen
+		 *       but this is the packet count, not the fragment count!
+		ifp->if_snd.ifq_maxlen	= sc->pvt.tx_ring_size;
+		*/
+		ifp->if_snd.ifq_maxlen	= ifqmaxlen;
+
+#ifdef  TSEC_DETACH_HACK
+		if ( !ifp->if_addrlist ) /* do only the first time [reattach hack] */
+#endif
+		{
+			if_attach(ifp);
+			ether_ifattach(ifp);
+		}
+
+	} else {
+#ifdef  TSEC_DETACH_HACK
+		if ( !ifp->if_init ) {
+			printk(DRVNAME": instance %i not attached.\n", unit);
+			return -1;
+		}
+		return tsec_detach(sc);
+#else
+		printk(DRVNAME": interface detaching not implemented\n");
+		return -1;
+#endif
+	}
+
+	return 0;
+}
+
+/* PHY stuff should really not be in this driver but separate :-(
+ * However, I don't have time right now to implement clean
+ * boundaries:
+ *  - PHY driver should only know about the PHY
+ *  - TSEC driver only provides MII access and knows
+ *    how to deal with a PHY interrupt.
+ *  - BSP knows how interrupts are routed. E.g., the MVME3100
+ *    shares a single IRQ line among 3 PHYs (for the three ports)
+ *    and provides a special 'on-board' register for determining
+ *    what PHY raised an interrupt w/o the need to do any MII IO.
+ * Integrating all these bits in a clean way is not as easy as
+ * just hacking away, sorry...
+ */
+
+/*
+ * Broadcom 54xx PHY register definitions. Unfriendly Broadcom doesn't
+ * release specs for their products :-( -- valuable info comes from
+ * the linux driver by
+ *    Maciej W. Rozycki <macro@linux-mips.org>
+ *    Amy Fong
+ */
+
+#define BCM54xx_GBCR	0x09		/* gigabit control    */
+#define BCM54xx_GBCR_FD		(1<<9)	/* full-duplex cap.   */
+
+#define BCM54xx_ECR		0x10		/* extended control   */
+#define BCM54xx_ECR_IM		(1<<12)	/* IRQ mask           */
+#define BCM54xx_ECR_IF		(1<<12)	/* IRQ force          */
+
+#define BCM54xx_ESR		0x11		/* extended status    */
+#define BCM54xx_ESR_IRQ	    (1<<12) /* IRQ pending        */
+
+#define BCM54xx_AUXCR	0x18		/* AUX control        */
+#define BCM54xx_AUXCR_PWR10BASET	(1<<2)
+
+#define BCM54xx_AUXST	0x19		/* AUX status         */
+#define BCM54xx_AUXST_LNKMM	(7<<8)	/* link mode mask     */
+
+/* link mode (linux' syngem_phy.c helped here...)
+ *
+ *  0: no link
+ *  1: 10BT    half
+ *  2: 10BT    full
+ *  3: 100BT   half
+ *  4: 100BT   half
+ *  5: 100BT   full
+ *  6: 1000BT  full
+ *  7: 1000BT  full
+ */
+
+#define BCM54xx_ISR		0x1a		/* IRQ status         */
+#define BCM54xx_IMR		0x1b		/* IRQ mask           */
+#define BCM54xx_IRQ_CRC		(1<< 0)	/* CRC error          */
+#define BCM54xx_IRQ_LNK		(1<< 1) /* LINK status chg.   */
+#define BCM54xx_IRQ_SPD		(1<< 2) /* SPEED change       */
+#define BCM54xx_IRQ_DUP		(1<< 3) /* LINK status chg.   */
+#define BCM54xx_IRQ_LRS		(1<< 4) /* Lcl. RX status chg.*/
+#define BCM54xx_IRQ_RRS		(1<< 5) /* Rem. RX status chg.*/
+#define BCM54xx_IRQ_SSE		(1<< 6) /* Scrambler sync err */
+#define BCM54xx_IRQ_UHCD	(1<< 7) /* Unsupp. HCD neg.   */
+#define BCM54xx_IRQ_NHCD	(1<< 8) /* No HCD             */
+#define BCM54xx_IRQ_HCDL	(1<< 9) /* No HCD Link        */
+#define BCM54xx_IRQ_ANPR	(1<<10) /* Aneg. pg. req.     */
+#define BCM54xx_IRQ_LC		(1<<11) /* All ctrs. < 128    */
+#define BCM54xx_IRQ_HC		(1<<12) /* Ctr > 32768        */
+#define BCM54xx_IRQ_MDIX	(1<<13) /* MDIX status chg.   */
+#define BCM54xx_IRQ_PSERR	(1<<14) /* Pair swap error    */
+
+#define PHY_IRQS ( BCM54xx_IRQ_LNK | BCM54xx_IRQ_SPD | BCM54xx_IRQ_DUP )
+
+
+static void
+phy_en_irq_at_phy( struct tsec_private *mp )
+{
+uint32_t ecr;
+
+	REGLOCK();
+	tsec_mdio_rd( 0, mp, BCM54xx_ECR, &ecr );
+	ecr &= ~BCM54xx_ECR_IM;
+	tsec_mdio_wr( 0, mp, BCM54xx_ECR,  ecr );
+	REGUNLOCK();
+}
+
+static void
+phy_dis_irq_at_phy( struct tsec_private *mp )
+{
+uint32_t ecr;
+
+	REGLOCK();
+	tsec_mdio_rd( 0, mp, BCM54xx_ECR, &ecr );
+	ecr |=  BCM54xx_ECR_IM;
+	tsec_mdio_wr( 0, mp, BCM54xx_ECR,  ecr );
+	REGUNLOCK();
+}
+
+static void
+phy_init_irq( int install, struct tsec_private *mp, void (*isr)(rtems_irq_hdl_param) )
+{
+uint32_t v;
+rtems_irq_connect_data xxx;
+
+	xxx.on     = noop;
+	xxx.off    = noop;
+	xxx.isOn   = nopf;
+	xxx.name   = BSP_PHY_IRQ;
+	xxx.handle = mp;
+	xxx.hdl    = isr;
+
+	phy_dis_irq_at_phy( mp );
+
+	REGLOCK();
+	/* Select IRQs we want */
+	tsec_mdio_wr( 0, mp, BCM54xx_IMR, ~ PHY_IRQS );
+	/* clear pending irqs */
+	tsec_mdio_rd( 0, mp, BCM54xx_ISR, &v );
+	REGUNLOCK();
+
+	/* install shared ISR */
+	if ( ! (install ?
+			BSP_install_rtems_shared_irq_handler( &xxx ) :
+			BSP_remove_rtems_irq_handler( &xxx )) ) {
+		rtems_panic( "Unable to %s shared irq handler (PHY)\n", install ? "install" : "remove" );
+	}
+}
+
+/* Because on the MVME3100 multiple PHYs (belonging to different
+ * TSEC instances) share a single interrupt line and we want
+ * to disable interrupts at the PIC rather than in the individual
+ * PHYs (because access to those is slow) we must implement
+ * nesting...
+ */
+STATIC int phy_irq_dis_level = 0;
+
+/* assume 'en_irq' / 'dis_irq' cannot be interrupted.
+ * Either because they are called from an ISR (all
+ * tsec + phy isrs must have the same priority) or
+ * from a IRQ-protected section of code
+ */
+
+static void
+phy_en_irq(struct tsec_private *mp)
+{
+	phy_irq_dis_level &= ~(1<<mp->unit);
+	if ( 0 == phy_irq_dis_level ) {
+		BSP_enable_irq_at_pic( BSP_PHY_IRQ );
+	}
+}
+
+
+static void
+phy_dis_irq(struct tsec_private *mp)
+{
+	phy_irq_dis_level |= (1<<mp->unit);
+	BSP_disable_irq_at_pic( BSP_PHY_IRQ );
+}
+
+static int
+phy_irq_pending(struct tsec_private *mp)
+{
+	/* MVME3100 speciality: we can check for a pending
+	 * PHY IRQ w/o having to access the MII bus :-)
+	 */
+	return in_8( BSP_MVME3100_IRQ_DETECT_REG ) & (1 << (mp->unit - 1));
+}
+
+static uint32_t
+phy_ack_irq(struct tsec_private *mp)
+{
+uint32_t v;
+
+	REGLOCK();
+	tsec_mdio_rd( 0, mp, BCM54xx_ISR, &v );
+	REGUNLOCK();
+
+#ifdef DEBUG
+	printf("phy_ack_irq: 0x%08"PRIx32"\n", v);
+#endif
+
+	return v;
+}
+
+#if defined(PARANOIA) || defined(DEBUG)
+
+static void dumpbd(TSEC_BD *bd)
+{
+		printf("Flags 0x%04"PRIx16", len 0x%04"PRIx16", buf 0x%08"PRIx32"\n",
+			bd_rdfl( bd ), ld_be16( &bd->len ), bd_rdbuf( bd ) );
+}
+
+void tsec_dump_rring(struct tsec_private *mp)
+{
+int i;
+TSEC_BD *bd;
+	if ( !mp ) {
+		printf("Neet tsec_private * arg\n");
+		return;
+	}
+	for ( i=0; i<mp->rx_ring_size; i++ ) {
+		bd = &mp->rx_ring[i];
+		printf("[%i]: ", i);
+		dumpbd(bd);
+	}
+}
+
+void tsec_dump_tring(struct tsec_private *mp)
+{
+int i;
+TSEC_BD *bd;
+	if ( !mp ) {
+		printf("Neet tsec_private * arg\n");
+		return;
+	}
+	for ( i=0; i<mp->tx_ring_size; i++ ) {
+		bd = &mp->tx_ring[i];
+		printf("[%i]: ", i);
+		dumpbd(bd);
+	}
+	printf("Avail: %i; Head %i; Tail %i\n", mp->tx_avail, mp->tx_head, mp->tx_tail);
+}
+#endif
+
+
+#ifdef DEBUG
+
+#undef free
+#undef malloc
+
+#include <stdlib.h>
+
+void cleanup_txbuf_test(void *u, void *a, int err)
+{
+	printf("cleanup_txbuf_test (releasing buf 0x%8p)\n", u);
+	free(u);
+	if ( err )
+		printf("cleanup_txbuf_test: an error was detected\n");
+}
+
+void *alloc_rxbuf_test(int *p_size, uintptr_t *p_data_addr)
+{
+void *rval;
+
+	*p_size = 1600;
+	rval    = malloc( *p_size + RX_BUF_ALIGNMENT );
+	*p_data_addr = (uintptr_t)ALIGNTO(rval,RX_BUF_ALIGNMENT);
+
+	/* PRIxPTR is still broken :-( */
+	printf("alloc_rxxbuf_test: allocated %i bytes @0x%8p/0x%08"PRIx32"\n",
+		*p_size, rval, (uint32_t)*p_data_addr);
+
+	return rval;
+}
+
+void consume_rxbuf_test(void *user_buf, void *consume_rxbuf_arg, int len)
+{
+int i;
+uintptr_t d = (uintptr_t)ALIGNTO(user_buf,RX_BUF_ALIGNMENT);
+
+	/* PRIxPTR is still broken */
+	printf("consuming rx buf 0x%8p (data@ 0x%08"PRIx32")\n",user_buf, (uint32_t)d);
+	if ( len > 32 )
+	   len = 32;
+	if ( len < 0 )
+		printf("consume_rxbuf_test: ERROR occurred: 0x%x\n", len);
+	else {
+		printf("RX:");
+		for ( i=0; i<len; i++ ) {
+			if ( 0 == (i&0xf) )
+				printf("\n  ");
+			printf("0x%02x ", ((char*)d)[i]);
+		}
+		printf("\n");
+		free(user_buf);
+	}
+}
+
+unsigned char pkt[100];
+
+void * tsec_up()
+{
+struct tsec_private *tsec =
+	BSP_tsec_setup( 1, 0,
+		cleanup_txbuf_test, 0,
+		alloc_rxbuf_test,
+		consume_rxbuf_test, 0,
+		 2,
+		 2,
+		 0);
+	BSP_tsec_init_hw(tsec, 0, 0);
+	memset(pkt,0,100);
+	memset(pkt,0xff,6);
+	BSP_tsec_read_eaddr(tsec, pkt+6);
+	pkt[12] = 0;
+	pkt[13] = 64;
+	return tsec;
+}
+
+#ifdef DEBUG_MODULAR
+int
+_cexpModuleInitialize(void*u)
+{
+extern int ifattach();
+extern int ifconf();
+extern int rtconf();
+	ifattach("ts1",rtems_tsec_attach,0);
+	ifconf("ts1","134.79.33.137","255.255.252.0");
+	ifconf("pcn1",0,0);
+	rtconf(0, "134.79.33.86",0,0);
+	return 0;
+}
+#endif
+#endif
