/*
 * Copyright (c) 1997, Chris Csanady,
 *                     Iowa State University Research Foundation, Inc.
 *
 * 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 unmodified, 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.
 *
 *	$Id: if_yfg.c,v 3.1 1997/07/04 03:30:29 ccsanady Exp $
 *
 * Notes:
 * This code is based on the fxp device driver written by David Greenman.
 */

/*
 *  Packet Engines G-NIC PCI Gigabit Ethernet driver
 */

#include "bpfilter.h"

#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/syslog.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>

#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#endif

#ifdef IPX
#include <netipx/ipx.h>
#include <netipx/ipx_if.h>
#endif

#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif

#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif

#include <vm/vm.h>			/* for vtophys */
#include <vm/vm_param.h>	/* for vtophys */
#include <vm/pmap.h>		/* for vtophys */
#include <machine/clock.h>	/* for DELAY */

#include <pci/pcivar.h>
#include <pci/if_yfgreg.h>

/*
 * Number of transmit descriptors.  This must be a power of two.
 */
#define YFG_NTXCB		128
#define YFG_TXCB_MASK   (YFG_NTXCB - 1)

/*
 * Number of receive buffers.  This must be a power of two.
 */
#define YFG_NRXDESC	32

struct yfg_softc {
	struct arpcom arpcom;		/* per-interface network data */
	struct yfg_csr *csr;		/* control/status registers */
	struct yfg_desc rx_desc_ring[YFG_NRXDESC];	/* Rx descriptor ring */
	struct yfg_rx_cb rx_ring[YFG_NRXDESC]; /* Rx control block ring */
	struct yfg_tx_cb *cbl_base;		/* base of TxCB list */
	struct yfg_tx_cb *cbl_first;	/* first active TxCB in list */
	struct yfg_tx_cb *cbl_last;		/* last active TxCB in list */
	unsigned int tx_queued;		/* # of active TxCB's */
	struct yfg_rx_cb *rx_stop;	/* stop command in receive list */
	struct mbuf *rx_headm;		/* head mbuf */
	struct mbuf *rx_tailm;		/* tail mbuf */
	int promisc_mode;			/* promiscuous mode enabled */
	int phy_primary_addr;		/* address of primary PHY */
	int phy_primary_device;		/* device type of primary PHY */
	int phy_1000Mbps_only;		/* PHY is 1000Mbps-only device */
};

static u_long yfg_count;

static char *yfg_probe		__P((pcici_t, pcidi_t));
static void yfg_attach		__P((pcici_t, int));
static void yfg_intr		__P((void *));
static void yfg_start		__P((struct ifnet *));
static int yfg_ioctl		__P((struct ifnet *, int, caddr_t));
static void yfg_init		__P((void *));
static void yfg_stop		__P((struct yfg_softc *));
static void yfg_watchdog	__P((struct ifnet *));
static int yfg_rx_alloc		__P((struct yfg_softc *, struct mbuf *));
static void yfg_shutdown	__P((int, void *));

int yfg_debug = 0;

static struct pci_device yfg_device = {
	"yfg",
	yfg_probe,
	yfg_attach,
	&yfg_count,
	NULL
};
DATA_SET(pcidevice_set, yfg_device);

/*
 * Descriptor list index masks. These are used to do list wrap-around.
 */
#define YFG_TXDESC_MASK	(YFG_NTXDESC - 1)
#define YFG_RXDESC_MASK	(YFG_NRXDESC - 1)

/*
 * Return identification string if this is device is ours.
 */
static char *
yfg_probe(config_id, device_id)
	pcici_t config_id;
	pcidi_t device_id;
{
	if (((device_id & 0xffff) == YFG_VENDORID_PKT_ENG) &&
	    ((device_id >> 16) & 0xffff) == YFG_DEVICEID_YELLOWFIN)
		return ("Packet Engines Yellowfin G-NIC");

	return NULL;
}

/*
 * Allocate data structures and attach the device.
 */
static void
yfg_attach(config_id, unit)
	pcici_t config_id;
	int unit;
{
	struct yfg_softc *sc;
	struct ifnet *ifp;
	vm_offset_t pbase;
	int s, i;
	u_long latency;
	u_short data;

	sc = malloc(sizeof(struct yfg_softc), M_DEVBUF, M_NOWAIT);
	if (sc == NULL)
		return;

	bzero(sc, sizeof(struct yfg_softc));

	s = splimp();

	/*
	 * Map control/status registers.
	 */
	if (!pci_map_mem(config_id, YFG_PCI_MMBA,
		(vm_offset_t *)&sc->csr, &pbase)) {
		printf("yfg%d: couldn't map memory\n", unit);
		goto fail;
	}

	/*
	 * Set latency if default unreasonable.
	 */
	latency = pci_conf_read(config_id, PCI_LATENCY_TIMER);
	latency = (latency >> 8) & 0xff;
	if (latency < 32) {
		printf("yfg%d: setting PCI bus latency to %d (was %d)\n",
			unit, 32, latency);
		pci_conf_write(config_id, PCI_LATENCY_TIMER, 32 << 8);
	} else if (bootverbose)
		printf("yfg%d: PCI bus latency is %d\n", unit, latency);

	/*
	 * Read MAC address
	 */
	bcopy((const void *)sc->csr->StationAddr, sc->arpcom.ac_enaddr, 6);
	printf("yfg%d: Ethernet address %6D\n", unit, sc->arpcom.ac_enaddr, ":");

	/*
	 * Reset to a stable state.
	 */
	sc->csr->DMAControl = DMA_SRST;
	DELAY(10);
	sc->csr->Config = SRST;
	sc->csr->Config = 0;
	DELAY(10);

	/*
	 * Allocate our interrupt.
	 */
	if (!pci_map_int(config_id, yfg_intr, sc, &net_imask)) {
		printf("yfg%d: couldn't map interrupt\n", unit);
		goto fail;
	}

	sc->cbl_base = malloc(sizeof(struct yfg_tx_cb) * YFG_NTXCB,
		M_DEVBUF, M_NOWAIT);
	if (sc->cbl_base == NULL)
		goto malloc_fail;

	/*
	 * Pre-allocate our receive buffers. 
	 */
	for (i = 0; i < YFG_NRXDESC; i++) {
		if (yfg_rx_alloc(sc, NULL) == 1) {
			goto malloc_fail;
		}
	}

	ifp = &sc->arpcom.ac_if;
	ifp->if_softc = sc;
	ifp->if_unit = unit;
	ifp->if_name = "yfg";
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX /*| IFF_MULTICAST*/;
	ifp->if_ioctl = yfg_ioctl;
	ifp->if_output = ether_output;
	ifp->if_start = yfg_start;
	ifp->if_watchdog = yfg_watchdog;
	ifp->if_baudrate = 1000000000;
	ifp->if_init = yfg_init;

	/*
	 * Attach the interface.
	 */
	if_attach(ifp);
	ether_ifattach(ifp);

#if NBPFILTER > 0
	bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif

	/*
	 * Add shutdown hook so that DMA is disabled prior to reboot. Not
	 * doing do could allow DMA to corrupt kernel memory during the
	 * reboot before the driver initializes.
	 */
	at_shutdown(yfg_shutdown, sc, SHUTDOWN_POST_SYNC);

	splx(s);
	return;

malloc_fail:
	printf("yfg%d: Failed to malloc memory\n", unit);
	(void) pci_unmap_int(config_id);
	if (sc && sc->cbl_base)
		free(sc->cbl_base, M_DEVBUF);
	if (sc && sc->rx_headm)
		m_freem(sc->rx_headm);
fail:
	if (sc)
		free(sc, M_DEVBUF);
	splx(s);
}

/*
 * Device shutdown routine. Called at system shutdown after sync. The
 * main purpose of this routine is to shut off receiver DMA so that
 * kernel memory doesn't get clobbered during warmboot.
 */
static void
yfg_shutdown(howto, sc)
	int howto;
	void *sc;
{
	yfg_stop((struct yfg_softc *) sc);
}

/*
 * Start packet transmission on the interface.
 */
static void
yfg_start(ifp)
	struct ifnet *ifp;
{

	struct yfg_softc *sc = ifp->if_softc;
	struct yfg_csr *csr = sc->csr;
	struct mbuf *m, *mb_head;
	struct yfg_tx_cb *txp;
	int i, segment, tx_int;

txloop:
	/*
	 * See if we're all filled up with buffers to transmit.
	 */
	if (sc->tx_queued >= YFG_NTXCB)
		return;
	tx_int = (sc->tx_queued > 64) ? INT_ALWAYS : 0;

	/*
	 * Grab a packet to transmit.
	 */
	IF_DEQUEUE(&ifp->if_snd, mb_head);
	if (mb_head == NULL) {
		/*
		* No more packets to send.
		*/
		return;
	}

	/*
	 * Get pointer to next available (unused) descriptor.
	 */
	txp = sc->cbl_last->next;

	/*
	 * Go through each of the mbufs in the chain and initialize
	 * the transmit descriptors with the physical address
	 * and size of the mbufs.
	 */
tdbinit:
	for (m = mb_head, segment = 0; m != NULL; m = m->m_next) {
		if (m->m_len != 0) {
			if (segment == YFG_NTXSEG - 2)
				break;
			txp->tdb[segment].reqCount = m->m_len;
			txp->tdb[segment].cmd = CMD_OUT_MORE;
			txp->tdb[segment].addr = vtophys(mtod(m, vm_offset_t));
			segment++;
		}
	}
	if (segment-- < 1)
		return;
	if (m != NULL) {
		struct mbuf *mn;

		/*
		 * We ran out of segments. We have to recopy this mbuf
		 * chain first.
		 */
		MGETHDR(mn, M_DONTWAIT, MT_DATA);
		if (mn == NULL) {
			m_freem(mb_head);
			return;
		}
		if (mb_head->m_pkthdr.len > MHLEN) {
			MCLGET(mn, M_DONTWAIT);
			if ((mn->m_flags & M_EXT) == 0) {
				m_freem(mn);
				m_freem(mb_head);
				return;
			}
		}
		m_copydata(mb_head, 0, mb_head->m_pkthdr.len, mtod(mn, caddr_t));
		mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len;
		m_freem(mb_head);
		mb_head = mn;
		goto tdbinit;
	}

	txp->mb_head = mb_head;

	/*
	 * Finish the initialization of this TxCB.
	 */
	txp->frame.status = 0;
	txp->tdb[segment].cmd = CMD_OUT_LAST | BRAN_ALWAYS;
/*	txp->tdb[YFG_NTXSEG - 2].cmd = CMD_IN_LAST | tx_int;*/
	txp->tdb[YFG_NTXSEG - 1].cmd = CMD_STOP;

	/*
	 * Advance the end-of-list forward.
	 */
	sc->cbl_last->tdb[YFG_NTXSEG - 1].baddr = vtophys(&txp->tdb[0]);
	sc->cbl_last->tdb[YFG_NTXSEG - 1].cmd = CMD_NOP | BRAN_ALWAYS;
	sc->cbl_last = txp;

	if (sc->tx_queued == 0)
		sc->cbl_first = txp;

	sc->tx_queued++;

	if (~csr->TxStatus & ACTIVE)
		csr->TxControl = SET_MASK(WAKE, WAKE);

#if NBPFILTER > 0
	/*
	 * Pass packet to bpf if there is a listener.
	 */
	if (ifp->if_bpf)
		bpf_mtap(ifp, mb_head);
#endif
	/*
	 * Set a 5 second timer just in case we don't hear from the
	 * card again.
	 */
	ifp->if_timer = 5;

	goto txloop;
}

/*
 * Process interface interrupts.
 */
static void
yfg_intr(arg)
	void *arg;
{
	struct yfg_softc *sc = arg;
	struct yfg_csr *csr = sc->csr;
	struct ifnet *ifp = &sc->arpcom.ac_if;
	u_int16_t int_status;

	if (yfg_debug)
		printf("IntStatus=%8.8x\n", csr->IntStatus);
	while (int_status = csr->IntClear) {
		/*
		 * Process transmit interrupts.
		 */
		if (int_status & INT_DIT) {
			struct yfg_tx_cb *txp;

			for (txp = sc->cbl_first;
				 txp->frame.status != 0; txp = txp->next) {
				if (txp->frame.status & FRAME_OK)
					ifp->if_opackets++;
				else
					ifp->if_collisions += (u_int8_t)
						txp->frame.status;
				if (txp->mb_head) {
					m_freem(txp->mb_head);
					txp->mb_head = NULL;
					sc->tx_queued--;
				}
				if (txp == sc->cbl_last)
					break;
			}
			sc->cbl_first = txp;
			ifp->if_timer = 0;
				
			/*
			 * Try to start more packets transmitting.
			 */
			if (ifp->if_snd.ifq_head != NULL)
				yfg_start(ifp);
		}

		/*
		 * Process receiver interrupts.
		 */
		if (int_status & (INT_ERI | INT_DIR)) {
			struct yfg_rx_cb *rxp;
rcvloop:
			rxp = sc->rx_stop->next;
			if (rxp->desc->xferStatus & EOP) {
				struct mbuf *m;

				/*
				 * Remove first packet from the chain.
				 */
				m = sc->rx_headm;
				sc->rx_headm = m->m_next;
				m->m_next = NULL;

				/*
				 * Add a new buffer to the receive chain. If this
				 * fails, the old buffer is recycled instead.
				 */
				if (yfg_rx_alloc(sc, m) == 0) {
					struct ether_header *eh;
					struct yfg_status *frame;
					u_int16_t len = rxp->desc->reqCount - rxp->desc->resCount;

					frame = (struct yfg_status *)(m->m_data + len - 4);
					if (~frame->status & FRAME_OK) {
						printf("yfg0: Rx frame error: %4.4x\n", frame->status);
						ifp->if_ierrors++;
						m_freem(m);
						goto rcvloop;
					}
					ifp->if_ipackets++;

					m->m_len = len - 8 - *(u_int8_t *)(m->m_data + len - 8);
					if (m->m_len < sizeof(struct ether_header)) {
						m_freem(m);
						goto rcvloop;
					}
					m->m_pkthdr.len = m->m_len;
					m->m_pkthdr.rcvif = ifp;
					eh = mtod(m, struct ether_header *);
#if NBPFILTER > 0
					if (ifp->if_bpf) {
						bpf_tap(ifp, mtod(m, caddr_t), len);
					}
#endif
					m->m_data += sizeof(struct ether_header);
					ether_input(ifp, eh, m);
				} else
					printf("yfg0: Rx frame dropped.\n");
				goto rcvloop;
			} else if (rxp->desc->xferStatus) {
				if (~rxp->desc->xferStatus & EOP)
					printf("yfg0: Rx xferStatus=%8.8x\n", rxp->desc->xferStatus);
			}
			/*
	 	 	* Wake up read channel.
	 	 	*/
		 	csr->RxControl = SET_MASK(WAKE, WAKE);
		}
	}
}

/*
 * Stop the interface. Cancels the statistics updater and resets
 * the interface.
 */
static void
yfg_stop(sc)
	struct yfg_softc *sc;
{
	struct ifnet *ifp = &sc->arpcom.ac_if;
	struct yfg_csr *csr = sc->csr;
	struct yfg_tx_cb *txp;
	int i;

	/*
	 * Reset to a stable state.
	 */
	csr->DMAControl = DMA_SRST;
	DELAY(10);
	csr->Config = SRST;
	csr->Config = 0;
	DELAY(10);

	/*
	 * Release any xmit buffers.
	 */
	for (txp = sc->cbl_first; txp != NULL && txp->mb_head != NULL;
		 txp = txp->next) {
		m_freem(txp->mb_head);
		txp->mb_head = NULL;
	}
	sc->tx_queued = 0;

	/*
	 * Free all the receive buffers then reallocate/reinitialize
	 */
	if (sc->rx_headm != NULL)
		m_freem(sc->rx_headm);
	sc->rx_headm = NULL;
	sc->rx_tailm = NULL;
	for (i = 0; i < YFG_NRXDESC - 2; i++)
		if (yfg_rx_alloc(sc, NULL) == 1) {
			/*                              
			 * This "can't happen" - we're at splimp()
			 * and we just freed all the buffers we need
			 * above.       
			 */
			panic("yfg_stop: no buffers!");
        }

	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
	ifp->if_timer = 0;
}

/*
 * Watchdog/transmission transmit timeout handler. Called when a
 * transmission is started on the interface, but no interrupt is
 * received before the timeout. This usually indicates that the
 * card has wedged for some reason.
 */
static void
yfg_watchdog(ifp)
	struct ifnet *ifp;
{
	struct yfg_softc *sc = ifp->if_softc;

	if (sc->csr->TxStatus & DEAD || sc->csr->RxStatus & DEAD) {
        log(LOG_ERR, "yfg%d: device timeout\n", ifp->if_unit);
		if(yfg_debug)
			printf("yfg%d: device timeout\n", ifp->if_unit);

        ifp->if_oerrors++;
        yfg_init(ifp->if_softc);
	}
}

static void
yfg_init(xsc)
	void *xsc;
{
	struct yfg_softc *sc = xsc;
	struct ifnet *ifp = &sc->arpcom.ac_if;
	struct yfg_csr *csr = sc->csr;
	struct yfg_tx_cb *txp;
	struct mbuf *m;
	int i, j, s;

	s = splimp();
	/*
	 * Cancel any pending I/O
	 */
	yfg_stop(sc);

	csr->TxIntSel = SET_MASK(TXABORT, TXABORT);
	csr->TxBranchSel = SET_MASK(TXABORT, TXABORT);
	csr->TxWaitSel = SET_MASK(TXSR, TXSR);
	csr->RxIntSel = SET_MASK(EOP, EOP);
	csr->RxBranchSel = SET_MASK(EOP, EOP);
	csr->RxWaitSel = SET_MASK(EOP, EOP);

	csr->DMAControl = DMA_BE | DMA_TDPE
		| 0x60						/* Tx burst size = 64	*/
		| 0x03						/* Rx burst size = 8	*/
		| 0x2 << 16					/* Rx arb priority		*/
		| 0x1 << 19;				/* Tx arb priority		*/
	csr->FlowControl = FC_TX | FC_RX
		| 0xFFFF;					/* Max Tx pause timer	*/
	csr->ExtFifoConfig = 0x60;		/* Rx = 2048K			*/
/*		| 0x0C;						/* Tx = 32K				*/

	/*	
	 * Accept all broadcast frames, reject all multicast. XXX
	 */
	csr->AddrFilterReg = 0x0001;
	csr->Config = RXEN|CRCE|FULLD|PADEN|CRCEN|SIFT|STRIP|PHY_10B;

	/*
	 * Initialize transmit descriptor list.
	 */
	txp = sc->cbl_base;
	bzero(txp, sizeof(struct yfg_tx_cb) * YFG_NTXCB);
	for (i = 0; i < YFG_NTXCB; i++) {
		txp[i].next = &txp[(i + 1) & YFG_TXCB_MASK];
		for (j = 0; j < YFG_NTXSEG - 2; j++) {
			txp[i].tdb[j].baddr =
				 vtophys(&txp[i].tdb[YFG_NTXSEG - 2]);
			txp[i].tdb[j].cmd = CMD_OUT_MORE | BRAN_TRUE;
		}
		txp[i].tdb[YFG_NTXSEG - 2].cmd = CMD_IN_LAST | INT_ALWAYS;
		txp[i].tdb[YFG_NTXSEG - 2].reqCount = sizeof(struct yfg_status);
		txp[i].tdb[YFG_NTXSEG - 2].addr = vtophys(&txp[i].frame);
	}
	sc->cbl_first = sc->cbl_last = txp;
	sc->cbl_first->tdb[YFG_NTXSEG - 1].cmd = CMD_STOP;
	sc->tx_queued = 0;

	/*
	 * Initialize receive descriptor list.
	 */
	bzero(sc->rx_desc_ring, sizeof(struct yfg_desc) * YFG_NRXDESC);
	for (i = 0, m = sc->rx_headm; i < YFG_NRXDESC - 2; i++, m = m->m_next) {
		sc->rx_desc_ring[i].reqCount = MCLBYTES;
		sc->rx_desc_ring[i].cmd = CMD_IN_MORE | INT_ALWAYS;
		sc->rx_desc_ring[i].addr = vtophys(mtod(m, vm_offset_t));
		sc->rx_ring[i].next = &sc->rx_ring[i + 1];
		sc->rx_ring[i].desc = &sc->rx_desc_ring[i];
	}
	sc->rx_stop = &sc->rx_ring[i];
	sc->rx_desc_ring[i].cmd = CMD_STOP;
	sc->rx_ring[i].next = &sc->rx_ring[0];
	sc->rx_ring[i].desc = &sc->rx_desc_ring[i];

	sc->rx_desc_ring[++i].cmd = CMD_NOP | BRAN_ALWAYS;
	sc->rx_desc_ring[i].baddr = vtophys(&sc->rx_desc_ring[0]);

	/*
	 * Set command pointers, and start DMA engines.
	 */
	csr->TxCmdPtr = vtophys(&txp->tdb[YFG_NTXSEG - 1]);
	csr->RxCmdPtr = vtophys(&sc->rx_desc_ring[0]);

	csr->IntEnable = INT_EN|INT_ALL;

	csr->TxControl = SET_MASK(RUN, RUN);
	csr->RxControl = SET_MASK(RUN, RUN);

	if (yfg_debug)
		printf("TxStatus=%x, RxStatus=%x\n", csr->TxStatus, csr->RxStatus);
		
	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;
	splx(s);
}

/*                               
 * Add a buffer to the end of the RX buffer list.
 * Return 0 if successful, 1 for failure. A failure results in
 * adding the 'oldm' (if non-NULL) on to the end of the list -
 * tossing out it's old contents and recycling it.
 */     
static int
yfg_rx_alloc(sc, oldm)
	struct yfg_softc *sc;
	struct mbuf *oldm;
{
	struct yfg_rx_cb *rxp;
	struct mbuf *m;

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m != NULL) {
		MCLGET(m, M_DONTWAIT);
		if ((m->m_flags & M_EXT) == 0) {
			m_freem(m);
			if (oldm == NULL)
				return 1;
			m = oldm;
			m->m_data = m->m_ext.ext_buf;
		}
	} else {
		if (oldm == NULL)
			return 1;
		m = oldm;
		m->m_data = m->m_ext.ext_buf;
	}

	if (sc->rx_headm != NULL)
		sc->rx_tailm->m_next = m;
	else
		sc->rx_headm = m;
	sc->rx_tailm = m;

	/*
	 * If there is no oldm, assume that we haven't set up the
	 * descriptor lists yet.
	 */
	if (!oldm)
		return (0);

	rxp = sc->rx_stop;
	rxp->next->desc->cmd = CMD_STOP;
	rxp->desc->reqCount = MCLBYTES;
	rxp->desc->xferStatus = 0;
	rxp->desc->addr = vtophys(m->m_ext.ext_buf);
	rxp->desc->cmd = CMD_IN_MORE | INT_ALWAYS;
	sc->rx_stop = rxp->next;

	return (m == oldm);
}

static int
yfg_ioctl(ifp, command, data)
	struct ifnet *ifp;
	int command;
	caddr_t data;
{
	struct ifaddr *ifa = (struct ifaddr *) data;
	struct yfg_softc *sc = ifp->if_softc;
	struct ifreq *ifr = (struct ifreq *) data;
	int s, error = 0;

	s = splimp();

	switch (command) {

	case SIOCSIFADDR:
	case SIOCGIFADDR:
	case SIOCSIFMTU:
		ether_ioctl(ifp, command, data);
		break;

	case SIOCSIFFLAGS:

		/*
		 * If interface is marked up and not running, then start it.
		 * If it is marked down and running, stop it.
		 * XXX If it's up then re-initialize it. This is so flags
		 * such as IFF_PROMISC are handled.
		 */
		if (ifp->if_flags & IFF_UP) {
			yfg_init(sc);
		} else {
			if (ifp->if_flags & IFF_RUNNING)
				yfg_stop(sc);
		}
		break;

	case SIOCADDMULTI:
	case SIOCDELMULTI:
		/*
		 * Multicast list has changed; set the hardware filter
		 * accordingly.
		 */
		yfg_init(sc);
		error = 0;
		break;

	default:
		error = EINVAL;
	}
	(void) splx(s);
	return (error);
}

