All pastes #297584 Raw Edit

Untitled

public text v1 · immutable
#297584 ·published 2006-12-31 04:39 UTC
rendered paste body
/***************************************************************************
 *
 * The contents of this file are furnished under licence, and may not be
 * copied or distributed without express written agreement.
 * Copyright © 2005 GE FANUC Embedded Systems, all rights reserved.
 *
 */

#pragma ident	"$Id: rxfob.c,v 1.47 2005/12/22 14:06:46 philip Exp $ RAMiX Inc."

/*
 * This is the Solaris fob driver to go on top of all our board that have
 * the fob firmware.
 * Unlike all other drivers (HP, ...), I have not been able to implement
 * a clean algorithm to post RX TCBs. Somehow, it rarely work (receiveing)
 * with pmc665 if I push more than one RX TCB at the beginning.
 * I have to slowly ramp up the number of RX TCBs.
 * Also on the GMS board (dual pentium), we are losing RX TCBs.
 * Since all TX TCBs seems to go through, I will assume the driver is not
 * receiving back the RX TCBs.  A possible work-around would be to check
 * if the received RX TCBs match the pointer (fob_rcurp). This pointer
 * just walked the descriptor list each time a valid RX TCBs comes back.
 */

/*
 * Includes, Declarations and Local Data
 */

#include	<sys/types.h>
#include 	<netinet/in.h>
#include	<sys/errno.h>
#include	<sys/debug.h>
#include	<sys/stropts.h>
#include	<sys/stream.h>
#include	<sys/strlog.h>
#include	<sys/cmn_err.h>

#include	<sys/kmem.h>
#include	<sys/conf.h>
#include	<sys/ksynch.h>
#include	<sys/stat.h>
#include	<sys/dlpi.h>
#include 	<sys/modctl.h>
#include 	<sys/sysmacros.h>
#include	<sys/stream.h>
#define KSTAT
#ifdef  KSTAT
#include	<sys/kstat.h>
#endif
#include 	<sys/dditypes.h>
#include	<sys/systm.h>
#include	<sys/ddi.h>
#include	<sys/sunddi.h>
#include	<sys/ethernet.h>
#include	"rxfob.h"
#include 	"iocp_nic.h"

static int mcdebug = 0;

#define RMX_CSR_WRITE(addr, value)                    \
        ddi_put32(fobp->fob_handle, (uint32_t *)&addr, value)

#define RMX_CSR_READ(addr)                    \
        ddi_get32(fobp->fob_handle, (uint32_t *)&addr)

#ifdef i386 /* useless */
#define get_ether_type(ptr) (\
        (((uint8_t *)&((struct ether_header *)ptr)->ether_type)[0] << 8) | \
        (((uint8_t *)&((struct ether_header *)ptr)->ether_type)[1]))
#else
#define get_ether_type(ptr) (\
        (((uint8_t *)&((struct ether_header *)ptr)->ether_type)[0] << 8) | \
        (((uint8_t *)&((struct ether_header *)ptr)->ether_type)[1]))
#endif
#define put_ether_type(ptr, value) {\
        ((uint8_t *)(&((struct ether_header *)ptr)->ether_type))[0] = \
                ((u_int)value & 0xff00) >> 8; \
        ((uint8_t *)(&((struct ether_header *)ptr)->ether_type))[1] = \
                (value & 0xff); }

#define GETPROP(dip, name, def)                /* shorthand ddi_getprop() */ \
        ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, name, def)
/*
 * Translate a kernel virtual address to i/o address.
 */
#define RMXIOPBIOADDR(fobp, a) \
        ((fobp)->fob_iopbiobase + (uint32_t)((uintptr_t)(a) - (fobp)->fob_iopbkbase))

/*
 * Translate a i/o address to kernel virtual address.
 */
#define RMXIOPBKADDR(fobp, a) \
        ((fobp)->fob_iopbkbase + (uint32_t)((uintptr_t)(a) - (fobp)->fob_iopbiobase))

/*
 * Function prototypes.
 */

static  int fobprobe(dev_info_t *);
static	int fobattach(dev_info_t *, ddi_attach_cmd_t);
static	int fobdetach(dev_info_t *, ddi_detach_cmd_t);
#ifdef KSTAT
static  void fobstatinit(struct fob *);
#endif
static	int fobinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static  int foballoc_dma_mem(struct fob *);
static  void fobfree_dma_mem(register struct fob *fobp, int count);
static	int fobopen(queue_t *, dev_t *, int, int, cred_t *);
static	int fobclose(queue_t *, int, cred_t *);
static	int fobwput(queue_t *, mblk_t *);
static	int fobwsrv(queue_t *);
static	void fobproto(queue_t *, mblk_t *);
static	void fobioctl(queue_t *, mblk_t *);
static  void fob_dl_ioc_hdr_info(queue_t *, mblk_t *);
static	void fobareq(queue_t *, mblk_t *);
static	void fobdreq(queue_t *, mblk_t *);
static	void fobdodetach(struct fobstr *);
static	void fobbreq(queue_t *, mblk_t *);
static	void fobubreq(queue_t *, mblk_t *);
static	void fobireq(queue_t *, mblk_t *);
static	void fobponreq(queue_t *, mblk_t *);
static	void fobpoffreq(queue_t *, mblk_t *);
static	void fobemreq(queue_t *, mblk_t *);
static	void fobdmreq(queue_t *, mblk_t *);
static	void fobpareq(queue_t *, mblk_t *);
static	void fobspareq(queue_t *, mblk_t *);
static	void fobudreq(queue_t *, mblk_t *);
static	int fobstart(queue_t *, mblk_t *, struct fob *);
static	uint32_t fobintr();
static	uint32_t fobintr675();
static	uint32_t fobintr696();
static  void fobmask_interrupt(register struct fob *);
static  void fobunmask_interrupt(register struct fob *);
static	void	fobreclaim(struct fob *);
static	void	fobwenable(struct fob *);
static	struct fobstr	*fobaccept(struct fobstr *, struct fob *, int,
	struct ether_addr *);
static  struct fobstr *fobpaccept(struct fobstr *, struct fob *, int,
        struct  ether_addr *);
static  void fobsetipq(struct fob *);
static  void fobread(struct fob *, volatile struct fobnic_pkt *);
static  void fobsendup(struct fob *, mblk_t *, struct fobstr *(*)());
static  mblk_t *fobaddudind(struct fob *, mblk_t *, struct ether_addr *,
	struct ether_addr *, int, uintptr_t);
static  int fobmcmatch(struct fobstr *, struct ether_addr *);
static	int fobinit(struct fob *);
static	void fobuninit(struct fob *);
static  struct fobbuf *fobgetbuf(struct fob *, int);
static  void fobfreebuf(struct fobbuf *);
static  void fobrmdinit(struct fob *, volatile struct fobnic_pkt *, struct fobbuf *);
static  void fob_rcv_error(struct fob *, struct fobnic_pkt *);
static  int fob_xmit_error(struct fob *, struct fobnic_pkt *);

/*static	void foberror(dev_info_t *,  const char *fmt, ...);*/
static	void foberror();
/*static void localetheraddr(struct ether_addr *hint, struct ether_addr *result);*/
static void fob_merror(queue_t *, mblk_t *, int);
static void fob_miocack(queue_t *, mblk_t *, int, int);
static void fob_miocnak(queue_t *, mblk_t *, int, int);
static mblk_t *fobexchange(queue_t *, mblk_t *, int, unsigned char, t_uscalar_t);
static int fob_msgsize(register mblk_t *mp);
static void fob_mcopymsg(mblk_t *mp, uint8_t *bufp);

static void fobdlbindack(queue_t *, mblk_t *, t_scalar_t, caddr_t,t_uscalar_t , int, t_uscalar_t);
static void fobdlokack(queue_t *, mblk_t *, t_uscalar_t);
static void fobdlerrorack(queue_t *, mblk_t *, t_uscalar_t, t_uscalar_t, t_uscalar_t);
static void fobdluderrorind(queue_t *, mblk_t *, u_char *, t_uscalar_t,
	t_uscalar_t, t_uscalar_t);
static void fobdlphysaddrack(queue_t *, mblk_t *, caddr_t, int);
static void fob_reset(struct fob *);
static int fobsetupIEC(struct fob *);
/*static char *ether_sprintf(struct ether_addr *addr);*/
static void  send_tcb(struct fob *, uint32_t);
static uint32_t rcv_tcb(struct fob *);
static void set_header(fobnic_msgh_t *, uint32_t, uint32_t);

static struct fob *fob_get_ptr(int);

/* function to read the srom */
static int fobread_macaddr(struct fob *, int);
static void convert(char *,int);
static  void flashData(queue_t *, mblk_t *);
#if 0
static uint32_t fob_crc32(uchar_t *, size_t);
#endif

#define _DDICT
#ifdef	_DDICT
#define	ENOSR	63	/* out of streams resources		*/
#endif

/* Data access requirements. */
static struct ddi_device_acc_attr	dev_attr = {
	DDI_DEVICE_ATTR_V0,
	DDI_STRUCTURE_LE_ACC,		/* little endian */
	DDI_STRICTORDER_ACC
};

/* On SPARC, the buffers should be native endianess. */
#ifdef sparc
static struct ddi_device_acc_attr buf_attr = {
	DDI_DEVICE_ATTR_V0,
	DDI_STRUCTURE_BE_ACC,	/* buffer endianess */
	DDI_STRICTORDER_ACC
};
#else /* i386 */
static struct ddi_device_acc_attr buf_attr = {
	DDI_DEVICE_ATTR_V0,
	DDI_NEVERSWAP_ACC,	/* buffer endianess */
	DDI_STRICTORDER_ACC
};
#endif


/* Local Static def's */
/*
 * 	Resource amounts. XXXXXXXXXXX
 */
static const int fob_ntmdp2 = FOB_NTMDP2; /* pow2 Transmit Ring Descriptors */
static const int fob_nrmdp2 = FOB_NRMDP2; /* pow22 Receive Ring Descriptors */
static const int fob_nbufs = FOB_NBUFS; /* # buffers allocated for xmit/recv pool */



/*
 * XXX Physical Medium broadcast address definition.
 */
static	const struct ether_addr etherbroadcastaddr = {
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

/*
 * MIB II broadcast/multicast packets
 */
#define IS_BROADCAST(ehp) \
        (ether_cmp(&ehp->ether_dhost, &etherbroadcastaddr) == 0)
#define IS_MULTICAST(ehp) \
        ((ehp->ether_dhost.ether_addr_octet[0] & 01) == 1)
#define BUMP_InNUcast(hmep, ehp) \
        if (IS_BROADCAST(ehp)) { \
            fobp->fob_brdcstrcv++; \
        } else if (IS_MULTICAST(ehp)) { \
            fobp->fob_multircv++; \
        }
#define BUMP_OutNUcast(hmep, ehp) \
        if (IS_BROADCAST(ehp)) { \
            fobp->fob_brdcstxmt++; \
        } else if (IS_MULTICAST(ehp)) { \
            fobp->fob_multixmt++; \
        }

/*
 * Linked list of "fob" structures - one per device.
 */
static	struct fob *fobdev = NULL;

/*
 * Linked list of active (inuse) driver Streams.
 */
static	struct	fobstr	*fobstrup = NULL;
static	krwlock_t	fobstruplock;

/* XXX Taken from ae.c. Will I use it? */
static	kmutex_t	foblock;
#ifdef WLOCK
static  kmutex_t	fobwenlock;
#endif

/*
 * Our dL_INFO_ACK template.
 */
static	dl_info_ack_t fobinfoack = {
	DL_INFO_ACK,			/* dl_primitive */
	ETHER_MTU,				/* dl_max_sdu */
	0,						/* dl_min_sdu */
	RMX_ADDRL,				/* dl_addr_length */
	DL_ETHER,				/* dl_mac_type */
	0,						/* dl_reserved */
	0,						/* dl_current_state */
	-2,						/* dl_sap_length */
	DL_CLDLS,				/* dl_service_mode */
	0,						/* dl_qos_length */
	0,						/* dl_qos_offset */
	0,						/* dl_range_length */
	0,						/* dl_range_offset */
	DL_STYLE2,				/* dl_provider_style */
	sizeof (dl_info_ack_t),		/* dl_addr_offset */
	DL_VERSION_2,				/* dl_version */
	ETHER_ADDRL,				/* dl_brdcst_addr_length */
	sizeof (dl_info_ack_t) + RMX_ADDRL,	/* dl_brdcst_addr_offset */
	0						/* dl_growth */
};

/*
 * Allocate and zero-out "number" structures
 * each of type "structure" in kernel memory.
 */
#define	GETSTRUCT(structure, number)   \
	((structure *) kmem_zalloc(\
		(u_int) (sizeof (structure) * (number)), KM_NOSLEEP))
/*
 * ddi_dma_sync() a buffer.
 */
#define RMXSYNCIOPB(fobp, a, size, who) \
        (void) ddi_dma_sync((fobp)->fob_iopb_hdl, \
                ((uintptr_t)(a) - (fobp)->fob_iopbkbase), (size), (who))

#define	RMXSAPMATCH(sap, type, flags) ((sap == type)? 1 : \
	((flags & SLALLSAP)? 1 : \
	((sap < ETHER_MTU) && (sap > 0) && (type < ETHER_MTU))? 1 : 0))

/*
 * Defining DEBUG on the compile line (-DDEBUG) will enable debugging
 * statements in this driver.
 * Error message control:
 * 0 = no messages.
 * 1 = Error messages.
 * Can set the value of the debug flag using adb or in /etc/system file
 * with "set fob:fobdebug=<value>"
 */
#define DEBUG
#ifdef  DEBUG
static	int	fobdebug = 1;
#else
static	int	fobdebug = 0;
#endif  DEBUG

static void *fob_state; 	/* opaque handle for state structs */

/* Standard Streams declarations */

static	struct	module_info	fobminfo = {
	RMXIDNUM,	/* mi_idnum */
	RMXNAME,	/* mi_idname */
	RMXMINPSZ,	/* mi_minpsz */
	RMXMAXPSZ,	/* mi_maxpsz */
	RMXHIWAT,	/* mi_hiwat */
	RMXLOWAT,	/* mi_lowat */
};

static	struct	qinit	fobrinit = {
	0,			/* qi_putp */
	0,			/* qi_srvp */
	fobopen,	/* qi_qopen */
	fobclose,	/* qi_qclose */
	0,			/* qi_qadmin */
	&fobminfo,	/* qi_minfo */
	NULL,		/* qi_mstat */
};

static	struct	qinit	fobwinit = {
	fobwput,	/* qi_putp */
	fobwsrv,	/* qi_srvp */
	0,			/* qi_qopen */
	0,			/* qi_qclose */
	0,			/* qi_qadmin */
	&fobminfo,	/* qi_minfo */
	NULL,		/* qi_mstat */
};

static struct	streamtab	fob_info = {
	&fobrinit,	/* st_rdinit */
	&fobwinit,	/* st_wrinit */
	NULL,		/* st_muxrinit */
	NULL,		/* st_muxwrinit */
};


/* Module Loading/Unloading and Autoconfiguration declarations */

/*
 * cb_ops contains the driver entry points and is roughly equivalent
 * to the cdevsw and bdevsw  structures in previous releases.
 *
 * dev_ops contains, in addition to the pointer to cb_ops, the routines
 * that support loading and unloading our driver.
 *
 * Unsupported entry points are set to nodev, except for the poll
 * routine , which is set to nochpoll(), a routine that returns ENXIO.
 */

static struct cb_ops fob_cb_ops = {
	nodev,			/* cb_open */
	nodev,			/* cb_close */
	nodev,			/* cb_strategy */
	nodev,			/* cb_print */
	nodev,			/* cb_dump */
	nodev,			/* cb_read */
	nodev,			/* cb_write */
	nodev,			/* cb_ioctl */
	nodev,			/* cb_devmap */
	nodev,			/* cb_mmap */
	nodev,			/* cb_segmap */
	nochpoll,		/* cb_chpoll */
	ddi_prop_op,	/* cb_prop_op */
	&fob_info,		/* cb_stream */
	D_MP | D_NEW	/* cb_flag */
};

/* XXX We won't take care of the power management for now. */
static struct dev_ops fob_ops = {
	DEVO_REV,		/* devo_rev */
	0,				/* devo_refcnt */
	fobinfo,		/* devo_getinfo */
	nulldev,		/* devo_identify */
	fobprobe,		/* devo_probe */
	fobattach,		/* devo_attach */
	fobdetach,		/* devo_detach */
	nodev,			/* devo_reset */
	&fob_cb_ops,	/* devo_cb_ops */
	(struct bus_ops *)NULL,	/* devo_bus_ops */
	NULL					/* devo_power */
};



/*
 * Module linkage information for the kernel.
 */
static struct modldrv modldrv = {
	&mod_driverops,	/* Type of module.  This one is a driver */
	"RAMiX FOB Driver 1.30.1",   /* Description */
	&fob_ops,	/* driver ops */
};

static struct modlinkage modlinkage = {
	MODREV_1, &modldrv, NULL
};


/*
 * Module Loading and Installation Routines.
 */

/*
 * Module Installation
 * Install the driver and "pre-allocate" space for instances of the driver
 */

int
_init(void)
{
	int status;
	ddi_iblock_cookie_t fob_cookie;

	if ((status = ddi_soft_state_init(&fob_state,
		sizeof (struct fob), 0)) != 0) {
		if (fobdebug)
			cmn_err(CE_CONT,
			    	"fob:_init - soft_state_init failed: 0x%x\n",
			    	status);
		return (status);
	}

	/* initialize global locks here */
	rw_init(&fobstruplock, "fob streams linked list lock",
			RW_DRIVER, (void *)NULL);
#ifdef WLOCK
	mutex_init(&fobwenlock, NULL, MUTEX_DRIVER, fob_cookie);
#endif
	status = mod_install(&modlinkage);
	if (status !=0) {
		rw_destroy(&fobstruplock);
	}

	return (status);
}

/*
 * Module Removal
 */

int
_fini(void)
{
	int status;

	/*
	 *  At this point, make sure you have canceled all call backs and
	 *  de-allocated all memory
	 */
	if ((status = mod_remove(&modlinkage)) != 0) {
		if (fobdebug)
			cmn_err(CE_CONT,
			    "fob:_fini- mod_remove failed: 0x%x\n", status);
		return (status);
	}
#ifdef WLOCK
	mutex_destroy(&fobwenlock);
#endif
	ddi_soft_state_fini(&fob_state);

	return (status);
}

/*
 * Return Module Info.
 */

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}


/*
 * ------------------------------------------------------
 *	Autoconfiguration Routines
 */

/*
 * Probe to see if device exists.
 * Note: probe() is required for only non-self-identifying devices.
 * Also Probe should not have any side-effects. Just check that this is the
 * right device. Do not set up any structures or initialize anything.
 */

/*ARGSUSED*/
static int
fobprobe(dip)
dev_info_t *dip;
{
	return (DDI_PROBE_SUCCESS);
}

/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.
 */

static int
fobattach(dip, cmd)
dev_info_t	*dip;
ddi_attach_cmd_t	cmd;
{
	register struct fob *fobp;	/* our state structure pointer. */
	volatile uint32_t *regsp;
	uint16_t command;			/* command register in conf. space */
	ddi_acc_handle_t conf_handle;	/* handle to configure PCI. */
	int instance;				/* instance number. */
	uint32_t regs, props, prop_len = sizeof(int);	/* register and properties. */
	char *prop;
	uint16_t svid, ssid;	/* vendor and system id. */
	uint32_t ix, jx;		/* index. */
	char  c[32];
	uint_t (*interrupt_routine)(caddr_t);

	if (cmd != DDI_ATTACH)
		return (DDI_FAILURE);

	/*
	 * Allocate soft data structure
	 */

	instance = ddi_get_instance(dip);

	if (ddi_soft_state_zalloc(fob_state, instance) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "soft state alloc: failed");
		return (DDI_FAILURE);
	}
	fobp = ddi_get_soft_state(fob_state, instance);

	fobp->instance = instance;
	/* Use this to send RX desc 1 by 1 then 8 by 8 to the fob firmware.
	 * Else ping take a long time to come.
	 */
	fobp->ramp = 0;
	fobp->local_mac_addr = 0;
	fobp->flashp = NULL;
        fobp->fpcp = NULL;
        fobp->ksp = NULL;
        fobp->mcp = NULL;
	fobp->fob_iopbkbase = NULL;
	if (fobdebug)
		cmn_err(CE_CONT,"attach:fobp = 0x%lx\n", fobp);

	/* Map I2O register. */
	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&regsp, 0, 0,
	    					&dev_attr, &fobp->fob_handle)) {
		if (fobdebug)
			cmn_err(CE_WARN,"ddi_map_regs failed");
		goto bad;
	}

	/* Master enable, memory enable and I/O enable. */
	if (pci_config_setup(dip, &conf_handle) != DDI_SUCCESS) {
		ddi_regs_map_free(&fobp->fob_handle);
		if (fobdebug)
			cmn_err(CE_WARN,"pci_config_setup failed");
		return (DDI_FAILURE);
	}
	fobp->pci_handle = conf_handle;
	/* save configuration register .*/
	fobp->cmd_reg = pci_config_get32(conf_handle, RMX_CFCS);
	fobp->base_reg = pci_config_get32(conf_handle, 0x10);

	if ((ddi_getlongprop(DDI_DEV_T_ANY, dip, 0, "local-mac-address?",
						 (caddr_t)&prop, (int *)&prop_len) == DDI_PROP_SUCCESS))
	{
		if (strncmp("true", prop, prop_len) == 0)
			fobp->local_mac_addr = 1;
		else
			fobp->local_mac_addr = 0;
		/* ddi_getlongprop allocates prop. */
		kmem_free(prop, prop_len);
	}
	/*
	 * If it is our board and the Ethernet chip was able to read
	 * the sub vendor id from the srom, it should pass this test.
	 *
	 */
	regs = pci_config_get32(conf_handle, 0);
	if (fobdebug)
		cmn_err(CE_WARN,"rxfob: 0x%x", regs);

	/*
	 * Add interrupt to system.
	 */
	if (regs == INTEL21555) { /* PMC675 serie */
		/* check it is a fob driver. */
		interrupt_routine = fobintr675;
		regs = pci_config_get32(conf_handle, RMX_CSID);
		if ((regs != RMX_SUBVID) && (regs != 0x675140b)) {
			cmn_err(CE_WARN,"Not a RAMiX svid");
			goto bad;
		}
		cmn_err(CE_CONT, "RAMiX PMC/RM675\n");
		fobp->boardid = 675;
	}
	else if (regs == RMX_SUBVID) { /* PMC66x serie with fob */
		fobp->boardid = i960;
		interrupt_routine = fobintr;
		/* get firmware release number */
       	regs = pci_config_get32(conf_handle, RMX_CSID);
		fobp->release = (regs&~0x80000000);
       	cmn_err(CE_CONT,"rxfob%d: Firmware release %d.%d\n",
				ddi_get_instance(dip), (regs&0xf000000) >> 24,
				(regs&0xff0000) >> 16);
	} 
    else if (regs == IBM440GX) { /* PMC696 */
		interrupt_routine = fobintr696;
        fobp->boardid = 696; /* 440GX  */
		/* get firmware release number */
       	regs = pci_config_get32(conf_handle, RMX_CSID);
		if ((regs != RMX_SUBVID)) {
			cmn_err(CE_WARN,"Not a RAMiX svid");
			goto bad;
		}
		cmn_err(CE_CONT, "RAMiX PMC696\n");
    }
    else {
		cmn_err(CE_WARN,"Not a Ramix svid");
		return (DDI_FAILURE);
	}


	command = pci_config_get32(conf_handle, RMX_CFCS);
	command |= (CMD_IOE | CMD_MEE | CMD_MSE);
	pci_config_put32(conf_handle, RMX_CFCS, command);
	/* read latentcy and cache line size. */
	command = pci_config_get32(conf_handle, 0x0c);
	command |= 0x4000;
	pci_config_put32(conf_handle, 0x0C, command);

	/*pci_config_teardown(&conf_handle); XXX */


	fobp->fob_regsp = regsp;
	fobp->fob_flags = 0;		/* zero the flags. */

	/*
 	 * Initialize condition variable.
	 */
	cv_init(&fobp->fob_cv, NULL, CV_DEFAULT, NULL);
	/*
	 * check to see if your device interrupts at a high level -
	 * if it does, you can a) print a msg saying you do not support high
	 * level interrupts OR b) you can write a high level interrupt handler.
	 * To see how to do this read the Solaris 2.x Writing Device Drivers
	 * manual
	 */

	/* stop the device: mask outbound interrupt/reset. */

	/* ae do not test that. */
	if (ddi_intr_hilevel(dip, 0) != 0) {
		if (fobdebug)
			cmn_err(CE_WARN,
			    	"debug: High level interrupts not supported");
	}

 	/*
     * Add interrupt to system.
     */
    if (fobdebug)
    	cmn_err(CE_CONT,"debug: ddi_add_intr\n");
    if (ddi_add_intr(dip, 0, &fobp->iblock_cookie, 0,
                     interrupt_routine, (caddr_t)fobp)) {
    	if (fobdebug)
    		cmn_err(CE_WARN,"ddi_add_intr failed");
        goto bad;
    }

	/*
	 * Initialize mutex's for this device.
	 */
	if (fobdebug)
		cmn_err(CE_CONT, "Init mutex\n");
	mutex_init(&fobp->fob_xmitlock, "fob xmit lock", MUTEX_DRIVER,
	    	(void *)fobp->iblock_cookie);
	mutex_init(&fobp->fob_intrlock, "fob intr lock", MUTEX_DRIVER,
	    	(void *)fobp->iblock_cookie);
	mutex_init(&fobp->fob_buflock," fob bufs lock", MUTEX_DRIVER,
				(void *)fobp->iblock_cookie);

	mutex_init(&fobp->fob_cvlock, "cv lock", MUTEX_DRIVER,
				(void *)fobp->iblock_cookie);

	/*
	 * Stuff private info into dip.
	 */
	ddi_set_driver_private(dip, (caddr_t)fobp);
	fobp->fob_dip = dip;

	/*
	 * Link this per-device structure in with the rest.
	 */
	mutex_enter(&foblock);
	fobp->fob_nextp = fobdev;
	fobdev = fobp;
	mutex_exit(&foblock);
	if (fobdebug)
		cmn_err(CE_CONT,"fobp = 0x%lx\n", fobdev);


	/*
	 * Create the filesystem device node.
	 */
	if (ddi_create_minor_node(dip, "rxfob", S_IFCHR,
		ddi_get_instance(dip), DDI_NT_NET, CLONE_DEV) == DDI_FAILURE) {
		if (fobdebug)
			cmn_err(CE_WARN, "ddi_create_minor_node failed");
		ddi_remove_minor_node(dip, NULL);
		goto bad;
	}

	/*
	 * Debug: Initialize the posted and received array
	 */
	for (ix = 0; ix < 128; ix++)
		for (jx = 0; jx < 2; jx++)
			fobp->postrcv[ix][jx] = 0;


	if (fobdebug)
		cmn_err(CE_CONT, "Get properties\n");
	/*fobdebug = GETPROP(dip, "fobdebug", 0); */
    /* disable/enable overwrite local-mac-address? */
	strcpy(c,"local-mac-address");
    props = GETPROP(fobp->fob_dip, c, 0);
    /*cmn_err(CE_CONT, "props local = 0x%x\n", props); */
    if (props == 0) { /* use OBP variable local-mac-address? */
		fobp->fob_flags &= ~LOCAL_MAC;
	    cmn_err(CE_CONT, "rxfob%d: local-mac-address? disabled\n",
	            instance);
            fobp->local_mac_addr = 1;
    }
    else
    {
        fobp->local_mac_addr = 0;
    	fobp->fob_flags |= LOCAL_MAC;
    }

    /* Alternate port */
	strcpy(c,"ap");
	convert(c, instance);
	props = GETPROP(fobp->fob_dip, c, 0);
	if (fobdebug)
		cmn_err(CE_CONT, "ap = %d\n", props);
	if (props == 1)
		fobp->fob_flags |= ALTERNATE;

	strcpy(c,"sp");
	convert(c, instance);
	props = GETPROP(dip, c, 10); /* XXX */
	if (props == 100) 
		fobp->fob_flags |= DCUSE100MBPS;
	else if (props == 10) 
		fobp->fob_flags |= DCUSE10MBPS;
	else 
		fobp->fob_flags |= AUTONEGOTIATION;
	if (fobdebug)
		cmn_err(CE_CONT,"props = %d, flag = 0x%x\n", props,
			fobp->fob_flags);

	if (fobdebug)
		cmn_err(CE_CONT, "Get DP\n");
	/* IGMP caching. */
	strcpy(c,"igmp");
	fobp->igmp = GETPROP(fobp->fob_dip, c, 0);
	if (fobp->igmp)
		cmn_err(CE_CONT, "IGMP caching enabled for interface %d\n",
				instance);
	strcpy(c,"dp");
	convert(c, instance);
	props = GETPROP(fobp->fob_dip, c, 0);
	if (props == 1) 
		fobp->fob_flags |= DCFULLDUPLEX;
	if (fobdebug)
		cmn_err(CE_CONT,"instance = %d, duplex = %d", instance, props);

	strcpy(c,"pm");
	convert(c, instance);
	props = GETPROP(fobp->fob_dip, c, 0);
	if (fobdebug)
		cmn_err(CE_CONT, "pm = %d\n", props);
	if (props == 1)
		fobp->fob_flags |= PRIME1;	/* port 1 is default. */
	else if (props == 2)
		fobp->fob_flags |= PRIME0; /* port 0 is default */

	strcpy(c,"wd");
	convert(c, instance);
	props = GETPROP(fobp->fob_dip, c, 0);
	if (fobdebug)
		cmn_err(CE_CONT, "wd = %d\n", props);
	if (props == 1) {
		fobp->fob_flags |= WDFAIL;
		strcpy(c,"wt");
		convert(c, instance);
		props = GETPROP(fobp->fob_dip, c, 100);
		/*cmn_err(CE_CONT, "wd timer = %d\n", props);*/
		if (props < 100) props = 100;
		fobp->wdtimer = props;
	}

	strcpy(c,"fb");
	convert(c, instance);
	props = GETPROP(fobp->fob_dip, c, 0);
    /* is failback timer enable */
	if (props == 1) {
		fobp->fob_flags |= WDFAIL;
		strcpy(c,"ft");
		convert(c, instance);
		props = GETPROP(fobp->fob_dip, c, 0);
		if (fobdebug)
			cmn_err(CE_CONT, "fb timer = %d\n", props);
		if (props < 0) props = 100;
		fobp->fbtimer = props;
	}
	/* property for ping notification. */
	strcpy(c,"pg");
	convert(c, instance);
	props = GETPROP(fobp->fob_dip, c, 0);
	if (fobdebug)
		cmn_err(CE_CONT, "pg = %d\n", props);
	if (props == 1)
		fobp->fob_flags |= PINGNOTE;
	if (fobdebug) {
		cmn_err(CE_CONT,"instance = %d, prime = %d", instance, props);
		cmn_err(CE_CONT, "fob_flags = 0x%x\n", fobp->fob_flags);
	}
	strcpy(c,"sg");
	convert(c, instance);
	props = GETPROP(fobp->fob_dip, c, 0);
	if (fobdebug)
		cmn_err(CE_CONT, "sg = %d\n", props);
	if (props == 1)
		fobp->fob_flags |= SGLFAULT;
#ifdef  KSTAT
	fobstatinit(fobp);
#endif  KSTAT
	fobp->fob_queued = 0; fobp->tx_queued = 0;
	ddi_report_dev(dip);

	if (fobdebug)
		cmn_err(CE_WARN,"debug: SUCCESS\n");
	return (DDI_SUCCESS);

bad:
	cmn_err(CE_WARN,"bad\n");
	if (fobp->fob_regsp)
		ddi_regs_map_free(&fobp->fob_handle);

	/* remove interrupts added to the system */
	if (fobp->iblock_cookie)
		ddi_remove_intr(dip, 0, fobp->iblock_cookie);

	/* destroy all mutex's */
	mutex_destroy(&fobp->fob_xmitlock);
	mutex_destroy(&fobp->fob_intrlock);
	mutex_destroy(&fobp->fob_buflock);

	if (fobp)
		ddi_soft_state_free(fob_state, instance);

	return (DDI_FAILURE);
}

/*
 * Detach - Free resources allocated in attach
 */

/*ARGSUSED*/
static int
fobdetach(dip, cmd)
dev_info_t		*dip;
ddi_detach_cmd_t	cmd;
{
	int instance;
	int ix;	/* counter */
	uint32_t cmdr, val; /* for command register bit setting/clearing. */
	register struct fob *fobp;
	struct fob *fobtmp, **prevfobp;


	if (fobdebug)
		cmn_err(CE_WARN, "detach");
	if (cmd != DDI_DETACH)
		return (DDI_FAILURE);


	instance = ddi_get_instance(dip);

	fobp = ddi_get_soft_state(fob_state, instance);
	if (fobp == NULL)
		return (DDI_FAILURE);

	/* reset the 664. */
/*********
	fobp->fob_flags &= ~INIT;
	pci_config_put32(fobp->pci_handle, -0x100+0x40, 0x20);
	drv_usecwait(1000);
	pci_config_put32(fobp->pci_handle, -0x100+0x40, 0x10);
	drv_usecwait(1000);
	ix = 0;
	while( ix < 100) {
		val = pci_config_get32(fobp->pci_handle, 0);
		if (val == 0xf0b140b) {
			cmn_err(CE_CONT, "Reset DONE\n");
			break;
		}
		drv_usecwait(10000);
	}
***********/
	/* restore registers */
	/*pci_config_put32(fobp->pci_handle, 0x10, fobp->base_reg);
	pci_config_put32(fobp->pci_handle, 0x04, fobp->cmd_reg);
	*/
	if (fobp->pci_handle) {
		pci_config_teardown(&fobp->pci_handle);
		fobp->pci_handle = NULL;
	}

	/* unmap device registers */
	if (fobp->fob_regsp)
		ddi_regs_map_free(&fobp->fob_handle);

	/* remove interrupts added to the system */
	if (fobp->iblock_cookie)
		ddi_remove_intr(dip, 0, fobp->iblock_cookie);

	kstat_delete(fobp->fob_ksp);

	if (fobp->fob_iopb_hdl) {
		/* free the buffers. */
		for (ix = 0; ix < fobp->fob_ntmds; ix++)
			if (fobp->fobtbufp[ix])
				fobfreebuf(fobp->fobtbufp[ix]);
		for (ix = 0; ix < fobp->fob_ntmds; ix++) /* as many mblk as TX descriptors. */

			if (fobp->fobtmblkp[ix])
				freemsg(fobp->fobtmblkp[ix]);

		for (ix = 0; ix < fobp->fob_nrmds; ix++)
			if (fobp->fobrbufp[ix])
				fobfreebuf(fobp->fobrbufp[ix]);
	} /* endif */
	/* XXX free DMA memory */
	fobfree_dma_mem(fobp, fobp->fob_nbufs);

#ifdef CV
	cv_destroy(&fobp->fob_cv);
#endif
	/* destroy all mutex's */
	mutex_destroy(&fobp->fob_xmitlock);
	mutex_destroy(&fobp->fob_intrlock);
	mutex_destroy(&fobp->fob_buflock);
	mutex_destroy(&fobp->fob_cvlock);

	/*
	 * unlink the device from the linked-list.
	 */
    mutex_enter(&foblock);  /* get mutex. */
	for (prevfobp = &fobdev; (fobtmp = *prevfobp) != NULL;
         prevfobp = &fobtmp->fob_nextp) {
		if (fobtmp == fobp)
			*prevfobp = fobtmp->fob_nextp;
	}
    mutex_exit(&foblock);  /* free mutex */

	/* release soft state */
	ddi_soft_state_free(fob_state, instance);

	/* release minor node */
	ddi_remove_minor_node(dip, NULL);

	return (DDI_SUCCESS);
}

/*
 * Translate "dev_t" to a pointer to the associated "dev_info_t".
 */
/* ARGSUSED */
static int
fobinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
	void **result)
{
	dev_t	dev;
	int	instance, rc;
	struct	fob	*fobp;


	dev = (dev_t)arg;
	instance = getminor(dev);

	switch (infocmd) {
	case DDI_INFO_DEVT2DEVINFO:
		/* XXX ae rewrite that. */
		fobp = ddi_get_soft_state(fob_state, instance);
		if (fobp == NULL) {
			*result = NULL;
			rc = DDI_FAILURE;
		} else {
			*result = fobp->fob_dip;
			rc = DDI_SUCCESS;
		}
		break;

	case DDI_INFO_DEVT2INSTANCE:
		*result = (void *)instance;
		rc = DDI_SUCCESS;
		break;

	default:
		rc = DDI_FAILURE;
		break;
	}
	return (rc);
}

/* XXX Free the DMA memory allocated for the buffers */
static void
fobfree_dma_mem(register struct fob *fobp, int count)
{
    int     size;
    int     i;
    struct fobbuf *lbp;

	if (fobdebug)
		cmn_err(CE_WARN,"inside fobfree_dma_mem: %d", count);
	if (fobp->fob_iopbkbase != NULL)
	{ /* XXX should test phys_addr */
    	for (i = 0; i < count; i++) {
			/* got address of buffer i */
			lbp = fobp->fob_buftab[i];
			if (lbp == NULL)
				break;
			ASSERT(lbp);

			(void) ddi_dma_unbind_handle(lbp->lb_buf_hdl);
			(void) ddi_dma_mem_free(&lbp->lb_acchdl);
			ddi_dma_free_handle(&lbp->lb_buf_hdl);
        }
	} /* endif */

	/*  free kernel memory */
	if (fobdebug)
		cmn_err(CE_WARN,"if (fobp->fob_buftab != NULL),0x%x ", fobp->fob_buftab);
	if (fobp->fob_buftab != NULL) {
        size = fobp->fob_nbufs * sizeof (struct fobbuf *);
        kmem_free(fobp->fob_buftab, size);
	    if (fobp->fob_iopb_hdl)
          	(void) ddi_dma_unbind_handle(fobp->fob_iopb_hdl);
	    if (fobp->fob_acchdl)
          	(void) ddi_dma_mem_free(&fobp->fob_acchdl);
	    if (fobp->fob_iopb_hdl)
           	ddi_dma_free_handle(&fobp->fob_iopb_hdl);
	    if (fobp->fob_flash_hdl)
           	(void) ddi_dma_unbind_handle(fobp->fob_flash_hdl);
	    if (fobp->fob_flahdl)
           	(void) ddi_dma_mem_free(&fobp->fob_flahdl);
	    if (fobp->fob_flash_hdl)
           	ddi_dma_free_handle(&fobp->fob_flash_hdl);
	}
	fobp->fob_iopbkbase = NULL;
}

/*
 * Allocate memory for:
 *   - transmit descriptor ring
 *   - receive descriptor ring
 *   - pool of xmit/recv buffers
 *
 * For SLAVE devices, allocate out of device local memory.
 * For DVMA devices, allocate out of main memory.
 *
 * The descriptors are allocated contiguously and the buffers are
 * allocated contiguously.  Each of the iopb and buf areas are
 * described by a DDI DMA handle,
 * (null for slave devices), the base kernel virtual address
 * for driver usage, and the base io virtual address
 * for the chip usage.
 */
static int
foballoc_dma_mem(register struct fob *fobp)
{
	struct  fobbuf   *lbp;
	uintptr_t a;
	int     size;
	int     i;
	ddi_dma_cookie_t cookie;
	ddi_dma_handle_t buf_hdl;
	ddi_dma_attr_t  fob_dma_attr;
	ddi_acc_handle_t acchdl;
	uint_t  count;
	size_t  rlen;

	fobp->fob_nrmdp2 = fob_nrmdp2;
	fobp->fob_nrmds = 1 << fobp->fob_nrmdp2;
	/*fobp->fob_nrmds = 80;*/
	fobp->fob_ntmdp2 = fob_ntmdp2;
	fobp->fob_ntmds = 1 << fobp->fob_ntmdp2;
	/*fobp->fob_ntmds = 48;*/
	fobp->fob_nbufs = fob_nbufs;
        cmn_err(CE_CONT, "nrmds=%d ntmds=%d nbufs=%d\n", fobp->fob_nrmds,
                fobp->fob_ntmds, fobp->fob_nbufs);
	/*
	 * Allocate the TX and RX TCBs area.
	 * all at one time remembering to allocate extra for
	 * alignments.
	 */
	size = (fobp->fob_nrmds * sizeof (struct fobnic_pkt)) +
		   (fobp->fob_ntmds * sizeof (struct fobnic_pkt));
	/*
	 * dynamically create a dma attribute structure
	 */
	fob_dma_attr.dma_attr_version = DMA_ATTR_V0;
	fob_dma_attr.dma_attr_addr_lo = 0;
	fob_dma_attr.dma_attr_addr_hi = 0xffffffff;
	fob_dma_attr.dma_attr_count_max = 0x00ffffff;
	fob_dma_attr.dma_attr_align = DESCRING_ALIGN;
	fob_dma_attr.dma_attr_burstsizes = 1;
	fob_dma_attr.dma_attr_minxfer = 1;
	fob_dma_attr.dma_attr_maxxfer = 0x00ffffff;
	fob_dma_attr.dma_attr_seg = 0xffffffff;
	fob_dma_attr.dma_attr_sgllen = 1;
	fob_dma_attr.dma_attr_granular = size;
#ifdef i386
	fob_dma_attr.dma_attr_granular = 1;
#endif
	fob_dma_attr.dma_attr_flags = 0;

	if ((i = ddi_dma_alloc_handle(fobp->fob_dip, &fob_dma_attr,
		DDI_DMA_SLEEP, NULL, &fobp->fob_iopb_hdl)) != DDI_SUCCESS) {
		cmn_err(CE_CONT, "A1\n");
		return (DDI_FAILURE);
	}
	/* it is little endian. */
	if (ddi_dma_mem_alloc(fobp->fob_iopb_hdl, (uint_t)size,
			&dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
			(caddr_t *)&fobp->fob_iopbkbase, &rlen, &fobp->fob_acchdl) !=
			DDI_SUCCESS) {
			cmn_err(CE_CONT, "A2\n");
			ddi_dma_free_handle(&fobp->fob_iopb_hdl);
			return (DDI_FAILURE);
	}

	if (ddi_dma_addr_bind_handle(fobp->fob_iopb_hdl, (struct as *)0,
					(caddr_t)fobp->fob_iopbkbase, (u_int)rlen,
					DDI_DMA_RDWR|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
					&cookie, &count) != DDI_SUCCESS) {
			cmn_err(CE_CONT, "A3\n");
			(void) ddi_dma_mem_free(&fobp->fob_acchdl);
			ddi_dma_free_handle(&fobp->fob_iopb_hdl);
			return (DDI_FAILURE);
	}
	fobp->fob_iopbiobase = cookie.dmac_address;
	if (fobdebug)
		cmn_err(CE_CONT,"fobp->fob_iopbiobase = 0x%x\n",
				fobp->fob_iopbiobase);

	/*
 	 * allocate area for binary data from user space and multicast address.
  	 */
	size = 4 * RMXROUNDUP(sizeof (struct fobnic_template), RMXBURSTSIZE);
	fob_dma_attr.dma_attr_granular = size;
#ifdef i386
	fob_dma_attr.dma_attr_granular = 1;
#endif

	if (ddi_dma_alloc_handle(fobp->fob_dip, &fob_dma_attr,
		DDI_DMA_SLEEP, NULL, &fobp->fob_flash_hdl) != DDI_SUCCESS) {
			return (DDI_FAILURE);
	}
	/* it is little endian. */
	if (ddi_dma_mem_alloc(fobp->fob_flash_hdl, (uint_t)size,
			&dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
			(caddr_t *)&fobp->flashp, &rlen, &fobp->fob_flahdl) !=
			DDI_SUCCESS) {
		ddi_dma_free_handle(&fobp->fob_flash_hdl);
		ddi_dma_free_handle(&fobp->fob_iopb_hdl);
		return (DDI_FAILURE);
	}

	if (ddi_dma_addr_bind_handle(fobp->fob_flash_hdl, (struct as *)0,
					(caddr_t)fobp->flashp, (u_int)rlen,
					DDI_DMA_RDWR|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
					&cookie, &count) != DDI_SUCCESS) {
			cmn_err(CE_CONT, "A6\n");
			(void) ddi_dma_mem_free(&fobp->fob_acchdl);
			(void) ddi_dma_mem_free(&fobp->fob_flahdl);
			ddi_dma_free_handle(&fobp->fob_flash_hdl);
			ddi_dma_free_handle(&fobp->fob_iopb_hdl);
			return (DDI_FAILURE);
	}
 	fobp->flashbase = cookie.dmac_address;
	if (fobdebug)
		cmn_err(CE_CONT, "fobp->flashbase = 0x%x\n", fobp->flashbase);

	/* set base of multicast structure. */
	fobp->mcbase = fobp->flashbase + sizeof (struct fobnic_template);
	fobp->mcp = (struct fobnic_addr *)((char *)(fobp->flashp) +
				sizeof (struct fobnic_template));

        /* set base of FLASHPC TCBs */
        fobp->fpcbase = fobp->mcbase + sizeof(struct fobnic_template);
        fobp->fpcp = (struct fobnic_template *)((char *)(fobp->mcp) +
				sizeof (struct fobnic_template));

        /* set base of kstat TCBs */
        fobp->ksbase = fobp->fpcbase + sizeof(struct fobnic_template);
        fobp->ksp = (struct fobnic_template *)((char *)(fobp->fpcp) +
				sizeof (struct fobnic_template));

	/* initialize address type field. */
	for (i = 0; i < RXN_MAXADDR_REQ; i++)
		fobp->mcp->addr[i].addrType = RXN_INVALID;

	/*
	 * Allocate buffer pointer stack (fifo).
	 */
	size = fobp->fob_nbufs * sizeof (struct fobnic_pkt *);
	fobp->fob_buftab = kmem_zalloc(size, KM_SLEEP);
	if (fobp->fob_buftab == NULL) {
		cmn_err(CE_CONT, "A7\n");
		(void) ddi_dma_unbind_handle(fobp->fob_iopb_hdl);
		(void) ddi_dma_mem_free(&fobp->fob_acchdl);
		ddi_dma_free_handle(&fobp->fob_iopb_hdl);
		return (DDI_FAILURE);
	}

/* allocate the buffer to tx/fob packet. */
	size = RMXROUNDUP((sizeof (struct fobbuf)), RMXBURSTSIZE);
	/*
 * dynamically create a dma attribute structure
	 */
	fob_dma_attr.dma_attr_version = DMA_ATTR_V0;
	fob_dma_attr.dma_attr_addr_lo = 0;
	fob_dma_attr.dma_attr_addr_hi = 0xffffffff;
	fob_dma_attr.dma_attr_count_max = 0x00ffffff;
	fob_dma_attr.dma_attr_align = RMXBURSTSIZE;
	fob_dma_attr.dma_attr_burstsizes = RMXBURSTSIZE;
	fob_dma_attr.dma_attr_minxfer = 1;
	fob_dma_attr.dma_attr_maxxfer = 0x00ffffff;
	fob_dma_attr.dma_attr_seg = 0xffffffff;
	fob_dma_attr.dma_attr_sgllen = 1;
	fob_dma_attr.dma_attr_granular = size;
#ifdef i386
	fob_dma_attr.dma_attr_align = 1;
#endif
	fob_dma_attr.dma_attr_flags = 0;

	/*
	 * Initialize buffer pointer stack, buffer-by-buffer.
	 */
	for (i = 0; i < fobp->fob_nbufs; i++) {
		/*
		 * IO map the buffers and get a "buffer" dma handle.
		 */
		if (ddi_dma_alloc_handle(fobp->fob_dip, &fob_dma_attr,
			DDI_DMA_SLEEP, NULL, &buf_hdl) != DDI_SUCCESS) {
			cmn_err(CE_CONT, "A8\n");
			fobfree_dma_mem(fobp, i);
			return (DDI_FAILURE);
		}

		/*
		 * Allocate the buffers burst-aligned.
		 */
		if (ddi_dma_mem_alloc(buf_hdl, (uint_t)size, &buf_attr, 
							  DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
							  (caddr_t *)&lbp, &rlen, &acchdl) != DDI_SUCCESS) {
			cmn_err(CE_CONT, "A9\n");
			ddi_dma_free_handle(&buf_hdl);
			fobfree_dma_mem(fobp, i);
			return (DDI_FAILURE);
		}

		if (ddi_dma_addr_bind_handle(buf_hdl, (struct as *)0,
			(caddr_t)lbp, rlen, DDI_DMA_CONSISTENT|DDI_DMA_RDWR,
			DDI_DMA_SLEEP, 0, &cookie, &count) != DDI_SUCCESS) {
			cmn_err(CE_CONT, "A10\n");
			(void) ddi_dma_mem_free(&acchdl);
			ddi_dma_free_handle(&buf_hdl);
			fobfree_dma_mem(fobp, i);
			return (DDI_FAILURE);
		}
		lbp->lb_fobp = fobp;
		lbp->lb_frtn.free_func = fobfreebuf;
		lbp->lb_frtn.free_arg = (char *)lbp;
		lbp->lb_buf_hdl = buf_hdl;
		lbp->lb_acchdl = acchdl;

		lbp->phys_addr = cookie.dmac_address;
/*cmn_err(CE_CONT,"lbp->phys_addr = 0x%x, rlen = %d\n",
	lbp->phys_addr, rlen); */
		fobp->fob_buftab[i] = lbp;
	} /* end for */
	fobp->fob_bufi = fobp->fob_nbufs;

	a = fobp->fob_iopbkbase;

	/* ring pointer for receive */
	fobp->fob_rmdp = (struct fobnic_pkt *)a;
 /*cmn_err(CE_CONT, "fobp->fob_rmdp = 0x%lx, 0x%x\n", fobp->fob_rmdp,
	RMXIOPBIOADDR(fobp, fobp->fob_rmdp));*/
	a += fobp->fob_nrmds * sizeof (struct fobnic_pkt);

	/* ring pointer for transmit */
	fobp->fob_tmdp = (struct fobnic_pkt *)a;
  /*cmn_err(CE_CONT, "fobp->fob_tmdp = 0x%lx, 0x%x\n", fobp->fob_tmdp,
	RMXIOPBIOADDR(fobp, fobp->fob_tmdp));*/
	/*
	 * Keep handy limit values for RMD, TMD, and Buffers.
	 */
	fobp->fob_rmdlimp = &((fobp->fob_rmdp)[fobp->fob_nrmds]);
	fobp->fob_tmdlimp = &((fobp->fob_tmdp)[fobp->fob_ntmds]);

	/*
	 * Zero out xmit and rcv holders.
	 */
	bzero((caddr_t)fobp->fobtbufp, sizeof (fobp->fobtbufp));
	bzero((caddr_t)fobp->fobtmblkp, sizeof (fobp->fobtmblkp));
	bzero((caddr_t)fobp->fobrbufp, sizeof (fobp->fobrbufp));

	/*if (fobp->fob_wantw)
			fobwenable(fobp); */
	if (fobdebug)
		cmn_err(CE_CONT,"foballoc: SUCCESS\n");
	return (DDI_SUCCESS);
}


#ifdef  KSTAT
/* XXX see later */
static int
fobstat_kstat_update(kstat_t *ksp, int rw)
{
	struct  fob		*fobp;
	struct  fobstat	*fobsp;
	struct  fobnic_info *tcbip;
        unsigned int status;
	uint32_t ix;


	if (rw == KSTAT_WRITE)
		return (EACCES);
	fobp = (struct fob *)ksp->ks_private;
	fobsp = (struct fobstat *)ksp->ks_data;

	if (fobp->fob_flags & RMXRUNNING) {
		mutex_enter(&fobp->fob_xmitlock);
		fobreclaim(fobp);
		mutex_exit(&fobp->fob_xmitlock);
		/*
		 * Get information on the link status.
		 */
		tcbip = (struct  fobnic_info *)fobp->ksp;
		/* get mutex */
		mutex_enter(&fobp->fob_cvlock);
		set_header((fobnic_msgh_t *)tcbip, RXN_STATS,
		           sizeof(struct fobnic_info)|1);
		/* sync cache for device */
        (void) ddi_dma_sync((fobp)->fob_flash_hdl,
                            3 * sizeof(struct fobnic_template),
                            sizeof(fobnic_msgh_t),
                            DDI_DMA_SYNC_FORDEV);
		/* send tcb. */
		/*cmn_err(CE_CONT, "flashbase = 0x%x\n", fobp->flashbase);*/
		send_tcb(fobp, fobp->ksbase);
		/*
		 * This can be annoying because information are now
		 * delayed.
		 */
		for(ix = 0; ix < 1; ix++)
		{
			/* wait until timeout. */
		    cv_timedwait(&fobp->fob_cv, &fobp->fob_cvlock,
		                 ddi_get_lbolt() + drv_usectohz(500000));
		    (void) ddi_dma_sync((fobp)->fob_flash_hdl,
                                        3 * sizeof(struct fobnic_template),
                                        sizeof(struct fobnic_force),
                                        DDI_DMA_SYNC_FORCPU);
		    if( tcbip->hdr.status != 0) break;
		}
                status = tcbip->hdr.status;
	    mutex_exit(&fobp->fob_cvlock);

	    if (status != RXN_OK) {
			fobp->fob_link0_status = -1;
			fobp->fob_link1_status = -1;
		}
	    else {
			fobp->fob_ifspeed = (tcbip->cur_prim&0x10000)?100000000ULL:10000000ULL;
			fobp->fob_link_duplex = (tcbip->cur_prim&0x20000)?1:0;
			fobp->fob_link0_status = (tcbip->cur_prim&0x100)?1:0;
			fobp->fob_link1_status = (tcbip->cur_prim&0x200)?1:0;
		}
	}

	fobsp->fk_ipackets.value.ul = fobp->fob_ipackets;
	fobsp->fk_ierrors.value.ul = fobp->fob_ierrors;
	fobsp->fk_opackets.value.ul = fobp->fob_opackets;
	fobsp->fk_oerrors.value.ul = fobp->fob_oerrors;
	fobsp->fk_collisions.value.ul = fobp->fob_collisions;
	fobsp->fk_ifspeed.value.ull = fobp->fob_ifspeed;
	fobsp->fk_link_duplex.value.ul = fobp->fob_link_duplex;

	fobsp->fk_link0_status.value.ul = fobp->fob_link0_status;
	fobsp->fk_link1_status.value.ul = fobp->fob_link1_status;
	fobsp->fk_link_status.value.ul = fobp->fob_link0_status
	  								 | fobp->fob_link1_status;

	fobsp->fk_rcvbytes.value.ul = fobp->fob_rcvbytes;
	fobsp->fk_xmtbytes.value.ul = fobp->fob_xmtbytes;
	fobsp->fk_multircv.value.ul = fobp->fob_multircv;
	fobsp->fk_multixmt.value.ul = fobp->fob_multixmt;
	fobsp->fk_brdcstrcv.value.ul = fobp->fob_brdcstrcv;
	fobsp->fk_brdcstxmt.value.ul = fobp->fob_brdcstxmt;
	fobsp->fk_norcvbuf.value.ul = fobp->fob_norcvbuf;
	fobsp->fk_noxmtbuf.value.ul = fobp->fob_noxmtbuf;

	fobsp->fk_opackets64.value.ul = fobp->fob_opackets64;
	fobsp->fk_ipackets64.value.ul = fobp->fob_ipackets64;
	fobsp->fk_rbytes64.value.ul = fobp->fob_rbytes64;
	fobsp->fk_obytes64.value.ul = fobp->fob_obytes64;

	fobsp->fk_nocanput.value.ul = fobp->fob_nocanput;
	fobsp->fk_kstat_update.value.ul = ++fobp->fob_kstat_update;
	return (0);
}

/* XXX see later */
static void
fobstatinit(struct  fob    *fobp)
{
	struct  kstat		*ksp;
	struct  fobstat	*fobsp;

	if ((ksp = kstat_create("rxfob", ddi_get_instance(fobp->fob_dip),
	NULL, "net", KSTAT_TYPE_NAMED,
	sizeof (struct fobstat) / sizeof (kstat_named_t), 0)) == NULL) {
		foberror(fobp->fob_dip, "kstat_create failed");
		return;
	}
	fobp->fob_ksp = ksp;
	fobsp = (struct fobstat *)(ksp->ks_data);
	kstat_named_init(&fobsp->fk_nocanput, "nocanput", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_kstat_update, "kstat_update", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_ipackets, "ipackets", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_ierrors, "ierrors", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_opackets, "opackets", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_oerrors, "oerrors", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_collisions, "collisions", KSTAT_DATA_ULONG);

	kstat_named_init(&fobsp->fk_ifspeed, "ifspeed", KSTAT_DATA_ULONGLONG);
	kstat_named_init(&fobsp->fk_link_duplex, "link_duplex", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_link_status, "link_status", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_link0_status, "link0_status", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_link1_status, "link1_status", KSTAT_DATA_ULONG);
	/*
	 * MIB II kstat variables
	 */
	kstat_named_init(&fobsp->fk_rcvbytes, "rbytes", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_xmtbytes, "obytes", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_multircv, "multircv", KSTAT_DATA_ULONG);
    kstat_named_init(&fobsp->fk_multixmt, "multixmt", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_brdcstrcv, "brdcstrcv", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_brdcstxmt, "brdcstxmt", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_norcvbuf, "norcvbuf", KSTAT_DATA_ULONG);
	kstat_named_init(&fobsp->fk_noxmtbuf, "noxmtbuf", KSTAT_DATA_ULONG);

	kstat_named_init(&fobsp->fk_ipackets64, "ipackets64", KSTAT_DATA_ULONGLONG);
    kstat_named_init(&fobsp->fk_opackets64, "opackets64", KSTAT_DATA_ULONGLONG);
    kstat_named_init(&fobsp->fk_rbytes64, "rbytes64", KSTAT_DATA_ULONGLONG);
    kstat_named_init(&fobsp->fk_obytes64, "obytes64", KSTAT_DATA_ULONGLONG);

	ksp->ks_update = fobstat_kstat_update;
	ksp->ks_private = (void *)fobp;
	kstat_install(ksp);
}

#endif	KSTAT

/*ARGSUSED*/
static
fobopen(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
{
	struct	fobstr	*slp;
	struct	fobstr	**prevslp;
	minor_t	minordev;
	int	rc = 0;

	ASSERT(rq);	/* from ae */
	ASSERT(sflag != MODOPEN);

	minordev = getminor(*devp);
	/*
	 * Serialize all driver open and closes.
	 */
	rw_enter(&fobstruplock, RW_WRITER);
#ifdef WLOCK
	mutex_enter(&fobwenlock);
#endif

	/*
	 * Determine minor device number.
	 */
	prevslp = &fobstrup;
	if (sflag == CLONEOPEN) {
		minordev = 0;
		for (; ((slp = *prevslp) != NULL); prevslp = &slp->sl_nextp) {
			if (minordev < slp->sl_minor)
				break;
			minordev++;
		}
		*devp = makedevice(getmajor(*devp), minordev);
	} else
		minordev = getminor(*devp);

	if (rq->q_ptr)
		goto done;

	if ((slp = GETSTRUCT(struct fobstr, 1)) == NULL) {
		rc = ENOMEM;
		rw_exit(&fobstruplock);
		return (rc);
	}

	slp->sl_minor = minordev;
	slp->sl_rq = rq;
	slp->sl_state = DL_UNATTACHED;
	slp->sl_sap = 0;
	slp->sl_flags = 0;
	slp->sl_fobp = NULL;
	slp->sl_mccount = 0;
	slp->sl_mctab = NULL;
	mutex_init(&slp->sl_lock, "fob stream lock", MUTEX_DRIVER, (void *)0);

	/*
	 * Link new entry into the list of active entries.
	 */
	slp->sl_nextp = *prevslp;
	*prevslp = slp;

	rq->q_ptr = WR(rq)->q_ptr = slp;

	/*
	 * Disable our write-side service procedure.
	 */
	noenable(WR(rq));
#ifdef WLOCK
	mutex_exit(&fobwenlock);
#endif
	rw_exit(&fobstruplock);
	qprocson(rq);
	return (rc);

done:
#ifdef WLOCK
	mutex_exit(&fobwenlock);
#endif
	rw_exit(&fobstruplock);
	qprocson(rq);
	return (rc);
}

/*ARGSUSED*/
static int
fobclose(queue_t *rq, int flag, cred_t *credp)
{
	struct	fobstr	*slp;
	struct	fobstr	**prevslp;

	ASSERT(rq);
	ASSERT(rq->q_ptr);
	if (fobdebug)
		cmn_err(CE_WARN,"fobclose");

	qprocsoff(rq);	/* XXX from ae. What does that do? */
	slp = (struct fobstr *)rq->q_ptr;

	/*
	 * Implicit detach Stream from interface.
	 */
	if (slp->sl_fobp)
		fobdodetach(slp);

	/* qprocsoff(rq); */ /* ae put a few lines above. Why? */

	rw_enter(&fobstruplock, RW_WRITER);
#ifdef WLOCK
	mutex_enter(&fobwenlock);
#endif


	/*
	 * Unlink the per-Stream entry from the active list and free it.
	 */
	for (prevslp = &fobstrup; ((slp = *prevslp) != NULL);
	    prevslp = &slp->sl_nextp)
		if (slp == (struct fobstr *)rq->q_ptr)
			break;
	ASSERT(slp);
	*prevslp = slp->sl_nextp;

	mutex_destroy(&slp->sl_lock);
	kmem_free(slp, sizeof (struct fobstr));

	rq->q_ptr = WR(rq)->q_ptr = NULL;
#ifdef WLOCK
	mutex_exit(&fobwenlock);
#endif
	rw_exit(&fobstruplock);
	return (0);
}

static
fobwput(queue_t *wq, mblk_t *mp)
{
	struct	fobstr	*slp = (struct fobstr *)wq->q_ptr;
	struct	fob	*fobp;

	switch (DB_TYPE(mp)) {
		case M_DATA:
			fobp = slp->sl_fobp;
			if ((slp->sl_flags & SLRAW == 0) ||
			    (slp->sl_state != DL_IDLE) ||
			    (fobp == NULL)) {
				fob_merror(wq, mp, EPROTO);
				break;
			}

			/*
			 * If any msgs already enqueued or the interface will
			 * loop back up the message (due to PROMISC), then
			 * enqueue the msg.  Otherwise just xmit it directly.
			 */
			if (wq->q_first) {
				if (fobdebug)
					cmn_err(CE_WARN,"wq->q_first");
				(void) putq(wq, mp);
				fobp->fob_wantw = 1;
				qenable(wq); /* hme */
			} else if (fobp->fob_flags & RMXPROMISC) {
				(void) putq(wq, mp);
				qenable(wq);
			} else
				(void) fobstart(wq, mp, fobp);

			break;

		case M_PROTO:
		case M_PCPROTO:
			/* XXX check interesting comment in ae.c */
			(void) putq(wq, mp);
			qenable(wq);
			break;

		case M_IOCTL:
			fobioctl(wq, mp);
			break;

		case M_FLUSH:
			if (*mp->b_rptr & FLUSHW) {
				flushq(wq, FLUSHALL);
				*mp->b_rptr &= ~FLUSHW;
			}
			if (*mp->b_rptr & FLUSHR)
				qreply(wq, mp);
			else
				freemsg(mp);
			break;

		default:
			freemsg(mp);
			break;
	}
	return (0);
}

/*
 * Enqueue M_PROTO/M_PCPROTO (always) and M_DATA (sometimes) on the wq.
 *
 * Processing of some of the M_PROTO/M_PCPROTO msgs involves acquiring
 * internal locks that are held across upstream putnext calls.
 * Specifically there's the problem of fobintr() holding fob_intrlock
 * and fobstruplock when it calls putnext() and that thread looping
 * back around to call fobwput and, eventually, fobinit() to create a
 * recursive lock panic.  There are two obvious ways of solving this
 * problem: (1) have fobintr() do putq instead of putnext which provides
 * the loopback "cutout" right at the rq, or (2) allow fobintr() to putnext
 * and put the loopback "cutout" around fobproto().  We choose the latter
 * for performance reasons.
 *
 * M_DATA messages are enqueued on the wq *only* when the xmit side
 * is out of buffer space.  Once the xmit resource is available again,
 * wsrv() is enabled and tries to xmit all the messages on the wq.
 */
static
fobwsrv(queue_t	*wq)
{
	mblk_t	*mp;
	struct	fobstr	*slp;
	struct	fob	*fobp;

	slp = (struct fobstr *)wq->q_ptr;
	fobp = slp->sl_fobp;

	if (fobdebug)
		cmn_err(CE_WARN,"fobwsrv: fobp = 0x%lx",fobp);
	while (mp = getq(wq))
		switch (DB_TYPE(mp)) {
			case	M_DATA:
				if (fobdebug)
					cmn_err(CE_WARN,"fobwsrv:M_DATA");
				if (fobp) {
					if (fobdebug)
						cmn_err(CE_WARN,"fobwsrv");
					if (fobstart(wq, mp, fobp))
						goto done;
				} else
					freemsg(mp);
				break;

			case	M_PROTO:
			case	M_PCPROTO:
				fobproto(wq, mp);
				break;

			default: /* nothing is working at ths point */
				ASSERT(0);
				break;
		} /* end switch */

done:
	return (0);
}


static void
fobproto(queue_t	*wq, mblk_t	*mp)
{
	union	DL_primitives	*dlp;
	struct	fobstr	*slp;
	t_uscalar_t	prim;

	if (fobdebug)
		cmn_err(CE_CONT, "fobproto\n");
	slp = (struct fobstr *)wq->q_ptr;
	dlp = (union DL_primitives *)mp->b_rptr;
	prim = dlp->dl_primitive;

	mutex_enter(&slp->sl_lock);

	switch (prim) {
		case	DL_UNITDATA_REQ:
			/* call fobstart */
			fobudreq(wq, mp);
			break;

		case	DL_ATTACH_REQ:
			fobareq(wq, mp);
			break;

		case	DL_DETACH_REQ:
			fobdreq(wq, mp);
			break;

		case	DL_BIND_REQ:
			fobbreq(wq, mp);
			break;

		case	DL_UNBIND_REQ:
			fobubreq(wq, mp);
			break;

		case	DL_INFO_REQ:
			fobireq(wq, mp);
			break;

		case	DL_PROMISCON_REQ:
			fobponreq(wq, mp);
			break;

		case	DL_PROMISCOFF_REQ:
			fobpoffreq(wq, mp);
			break;

		case	DL_ENABMULTI_REQ:
			fobemreq(wq, mp);
			break;

		case	DL_DISABMULTI_REQ:
			fobdmreq(wq, mp);
			break;

		case	DL_PHYS_ADDR_REQ:
			fobpareq(wq, mp);
			break;

		case	DL_SET_PHYS_ADDR_REQ:
			fobspareq(wq, mp);
			break;

		default:
			fobdlerrorack(wq, mp, prim, DL_UNSUPPORTED, 0);
			break;
	}

	mutex_exit(&slp->sl_lock);
}

static void
fobioctl(queue_t *wq, mblk_t *mp)
{
	struct	iocblk	*iocp = (struct iocblk *)mp->b_rptr;
	struct	fobstr	*slp = (struct fobstr *)wq->q_ptr;
 	struct  fobnic_msgh *tcbp;
	struct  fobnic_template *tpp;
	struct  fobnic_force *tcbfp;
	struct  fobnic_info *tcbip;
	uint32_t *ptr, instance; /* store instance number. */
	uint32_t port;
	int ix; /* index counter. */
 	register struct fob *fobp;
        unsigned int status;

	if (fobdebug)
		cmn_err(CE_WARN, "ioctl");
	switch (iocp->ioc_cmd) {
	case RXN_STATS:
		if (mp->b_cont == NULL) {
					fob_miocnak(wq, mp, 0, EINVAL);
			return;
		}
		ptr = (uint32_t *)mp->b_cont->b_rptr;
		instance = *ptr; /* interface number */
		/* get soft state structure. */
		fobp = fob_get_ptr(instance);

		if (fobp == NULL) {
			fob_miocnak(wq, mp, 0, EIO);
			return;
		}
		ptr++;
		/*
		 * Memory may not have been allocated yet.
		 */
		if (fobp->flashp == NULL) {
			fob_miocnak(wq, mp, 0, EIO);
			return;
		}
		if (mp->b_cont)
			freemsg(mp->b_cont);
		mp->b_cont = allocb(sizeof(struct fobnic_info)-16, BPRI_MED);
		if (mp->b_cont == (mblk_t *)NULL) {
			fob_miocnak(wq, mp, 0, EAGAIN);
			return;
		}


		/* pointer to an available TCB area. */
		tcbip = (struct  fobnic_info *)fobp->flashp;

		/* get mutex */
		mutex_enter(&fobp->fob_cvlock);
		set_header((fobnic_msgh_t *)tcbip, RXN_STATS,
					sizeof(struct fobnic_info));
		/* sync cache for device */
		(void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
						sizeof(struct fobnic_info), DDI_DMA_SYNC_FORDEV);
		/* send tcb. */
		send_tcb(fobp, fobp->flashbase);

		for(ix = 0; ix < 400; ix++){
			/* wait until timeout. */
			cv_timedwait(&fobp->fob_cv, &fobp->fob_cvlock,
						 ddi_get_lbolt() +
						 drv_usectohz(10000));
			(void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
								sizeof(struct fobnic_force),
								DDI_DMA_SYNC_FORCPU);
			if( tcbip->hdr.status != 0) break;
		}
		if (tcbip->hdr.status != RXN_OK) {
			fob_miocnak(wq, mp, 0, EINVAL);
		}
		else {
			/* 0x20 = SPACE */
			if (fobp->boardid == i960)
				tcbip->version = fobp->release|0x20;
			/*cmn_err(CE_CONT, "mac0 = 0x%x, mac1 = 0x%x, version = 0x%x, transition = %d\n", tcbip->macaddr[0], tcbip->macaddr[1], tcbip->version, tcbip->num_transistions);*/
			ptr = (uint32_t *)mp->b_cont->b_rptr;
			bcopy(&tcbip->macaddr[0], ptr,
				  sizeof(struct fobnic_info) - 16);
			mp->b_cont->b_wptr = mp->b_cont->b_rptr +
			sizeof(struct fobnic_info) - 16;
			fob_miocack(wq, mp, sizeof(struct fobnic_info) - 16, 0);
		}
		mutex_exit(&fobp->fob_cvlock);
		break;
	case RXN_FORCEPORT:
		if (mp->b_cont == NULL) {
					fob_miocnak(wq, mp, 0, EINVAL);
			return;
		}
			ptr = (uint32_t *)mp->b_cont->b_rptr;
		instance = *ptr; /* interface number */
		/* get soft state structure. */
		fobp = fob_get_ptr(instance);

		if (fobp == NULL) {
			fob_miocnak(wq, mp, 0, EIO);
			return;
		}
		/*
		 * Memory may not have been allocated yet.
		 */
		if (fobp->flashp == NULL) {
			fob_miocnak(wq, mp, 0, EIO);
			return;
		}
		ptr++;
		port = *ptr;	/* port number */

		/* pointer to an available TCB area. */
		tcbfp = (struct  fobnic_force *)fobp->flashp;
		/* get mutex */
		mutex_enter(&fobp->fob_cvlock);
		/* set the header. */
		set_header((fobnic_msgh_t *)tcbfp, RXN_FORCEPORT,
				  sizeof(struct fobnic_force));
		/*
		 * set up parameters.
		 */
		tcbfp->port = port & 0x1;
		tcbfp->override = (port >> 1) & 0x1;
		/* sync cache for device */
		(void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
					sizeof(struct fobnic_force), DDI_DMA_SYNC_FORDEV);
		/* send tcb. */
		send_tcb(fobp, fobp->flashbase);
#ifndef CV
		for(ix = 0; ix < 400; ix++)
		{
			/* wait until timeout. */
			cv_timedwait(&fobp->fob_cv, &fobp->fob_cvlock,
						 ddi_get_lbolt() +
						 drv_usectohz(10000));
			(void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
								sizeof(struct fobnic_force),
								DDI_DMA_SYNC_FORCPU);
			if( tcbfp->hdr.status != 0) break;
		}
                status = tcbfp->hdr.status;
		mutex_exit(&fobp->fob_cvlock);
#else
		(void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
						sizeof(struct fobnic_force), DDI_DMA_SYNC_FORCPU);
#endif
		if ((status != 0x100) && (status != RXN_OK))
				fob_miocnak(wq, mp, 0, EINVAL);
		else
				fob_miocack(wq, mp, 0, 0);
#ifdef CV
		mutex_exit(&fobp->fob_cvlock);
#endif
	break;

	case RXN_DEBUG:
		if (fobdebug == 0)
			fobdebug = 1;
		else
			fobdebug = 0;
#if 1
		if (mp->b_cont == NULL) {
			fob_miocnak(wq, mp, 0, EINVAL);
			return;
		}
		ptr = (uint32_t *)mp->b_cont->b_rptr;
		instance = *ptr; /* interface number */
		/* get soft state structure. */
		fobp = fob_get_ptr(instance);

		if (fobp == NULL) {
			fob_miocnak(wq, mp, 0, EIO);
			return;
		}
		cmn_err(CE_CONT, "fobp->fob_nrmds = %d\n", fobp->fob_nrmds);

		for (ix = 0; ix < fobp->fob_nrmds; ix++)
			cmn_err(CE_CONT, "%d: posted: %d, received: %d\n", ix,
				fobp->postrcv[ix][0], fobp->postrcv[ix][1]);

		if (fobp != NULL)
			cmn_err(CE_CONT, "fob0:tx = %d, fob = %d\n",
				fobp->tx_queued, fobp->fob_queued);
				fob_miocack(wq, mp, 0, 0);
				break;
#endif
	case RXN_CONFIG:
			if (mp->b_cont == NULL) {
					fob_miocnak(wq, mp, 0, EINVAL);
			return;
		}
			ptr = (uint32_t *)mp->b_cont->b_rptr;
		instance = *ptr;
		/* get soft state structure. */
		fobp = ddi_get_soft_state(fob_state, instance);
		if (fobp == NULL) {
			fob_miocnak(wq, mp, 0, EIO);
			return;
		}
		/*
		 * Memory may not have been allocated yet.
		 */
		if (fobp->flashp == NULL) {
			fob_miocnak(wq, mp, 0, EIO);
			return;
		}
		cmn_err(CE_CONT, "queued = %d\n", fobp->fob_queued);
	/*	goto ack;*/
		tpp = (struct fobnic_template *)fobp->flashp;
		/* get mutex */
		mutex_enter(&fobp->fob_cvlock);
		/* set the header. */
		set_header((fobnic_msgh_t *)tpp, RXN_CONFIG,
				  sizeof(struct fobnic_template));
		tpp->private[0] = 2;
		/* sync cache for device */
			(void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
						sizeof(struct fobnic_msgh), DDI_DMA_SYNC_FORDEV);
		/* send tcb. */
		send_tcb(fobp, fobp->flashbase);
		/* wait until done ot timeout. */
		/*ix = 0;
				while (tpp->hdr.status == 0) {
						ix++;
						drv_usecwait(1000);
			if (tpp->hdr.status != 0)
				break;
						if (ix == 0x100)
								break;
				}*/
	ack:
		mutex_exit(&fobp->fob_cvlock);
		/* acknowledge.*/
		fob_miocack(wq, mp, 0, 0);
		break;
	case RXN_FLASHPC:
		flashData(wq, mp);
		break;
	case RXN_FLASHGO:
			if (mp->b_cont == NULL) {
					fob_miocnak(wq, mp, 0, EINVAL);
			return;
		}
			ptr = (uint32_t *)mp->b_cont->b_rptr;
		instance = *ptr;
		cmn_err(CE_CONT,"flash GO: %d\n", instance);
		/* get soft state structure. */
		fobp = ddi_get_soft_state(fob_state, instance);
		if (fobp == NULL) {
			fob_miocnak(wq, mp, 0, EINVAL);
			return;
		}
		tcbp = (struct  fobnic_msgh *)fobp->flashp;
		/* get mutex */
		mutex_enter(&fobp->fob_cvlock);
		/* 6 is the flash program command. */
		set_header((fobnic_msgh_t *)tcbp, RXN_FLASHGO, sizeof(struct fobnic_msgh));
		/* sync cache for device */
	   (void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
						sizeof(struct fobnic_msgh), DDI_DMA_SYNC_FORDEV);
		/* send tcb. */
		send_tcb(fobp, fobp->flashbase);
		/* wait until done ot timeout. */
		ix = 0;
		while (tcbp->status == 0) {
			/* sync cache for device */
			(void) ddi_dma_sync((fobp)->fob_flash_hdl, 0,
			sizeof(struct fobnic_msgh), DDI_DMA_SYNC_FORCPU);
			ix++;
			drv_usecwait(50000);
			if (tcbp->status != 0)
				break;

			if (ix == 0x1000)
				break;
		}
		cmn_err(CE_CONT, "flash programming done:0x%x\n", tcbp->status);
                status = tcbp->status;
		mutex_exit(&fobp->fob_cvlock);
		if ((status != 0x100) && (status != RXN_OK))
			fob_miocnak(wq, mp, 0, EINVAL);
		else
			fob_miocack(wq, mp, 0, 0);
		break;
	case DLIOCRAW:		/* raw M_DATA mode */
		if (fobdebug)
			cmn_err(CE_CONT, "@1");
		slp->sl_flags |= SLRAW;
		fob_miocack(wq, mp, 0, 0);
		break;
	/* XXX from ae. */
	case DL_IOC_HDR_INFO:	/* M_DATA "fastpath" info request */
		if (fobdebug)
			cmn_err(CE_CONT, "@2");
		  fob_dl_ioc_hdr_info(wq, mp);
		  break;
	default:
	if (fobdebug)
		cmn_err(CE_CONT, "@3");
		fob_miocnak(wq, mp, 0, EINVAL);
		break;
	}
}

static void
flashData(wq, mp)
queue_t *wq;
mblk_t  *mp;
{
 int ix;	/* counter */
 struct fobnic_template *tcbp;
 uint32_t *ptr;
 uint32_t instance, offset; /* instance number, offset in binary. */
 register struct fob *fobp;

	if (mp->b_cont == NULL) {
            cmn_err(CE_WARN, "Failed flash ioctl, mp->b_cont == NULL\n");
            fob_miocnak(wq, mp, 0, EINVAL);
            return;
	}
        ptr = (uint32_t *)mp->b_cont->b_rptr;
        instance = *ptr;
	/* get soft state structure. */
	fobp = ddi_get_soft_state(fob_state, instance);
	if (fobp == NULL) {
            cmn_err(CE_WARN, "Failed flash ioctl, fobp == NULL\n");
            fob_miocnak(wq, mp, 0, EINVAL);
            return;
	}
        ptr++;
        offset = *ptr;
        ptr++;  /* point to the data area. */

	/* get pointer to tcb area. */
	tcbp = fobp->fpcp;
	if (tcbp == NULL) {
            cmn_err(CE_WARN, "Failed flash ioctl, tcbp == NULL\n");
            fob_miocnak(wq, mp, 0, EIO);
            return;
        }
        /* get mutex */
        mutex_enter(&fobp->fob_cvlock);
	bzero(tcbp, sizeof(struct fobnic_template));
	if (fobdebug)
		cmn_err(CE_CONT, "fobp->fpcp = 0x%lx\n", fobp->fpcp);

	/* set the header. */
	set_header((fobnic_msgh_t *)tcbp, RXN_FLASHPC,
		    sizeof(struct fobnic_template));
	tcbp->private[0] = offset;
	bcopy((caddr_t)ptr, &tcbp->private[1], 19*sizeof(int));
        if (ddi_dma_sync((fobp)->fob_flash_hdl,
                         2 * sizeof(struct fobnic_template),
                         sizeof(struct fobnic_template), DDI_DMA_SYNC_FORDEV)
            != DDI_SUCCESS)
        {
            mutex_exit(&fobp->fob_cvlock);
            cmn_err(CE_WARN, "Could not sync flash TCB for DEV\n");
            fob_miocnak(wq, mp, 0, EIO);
            return;
        }

	send_tcb(fobp, fobp->fpcbase);
	ix = 0;
	while (ix < 2000) {
		ix++;
		drv_usecwait(200);
		/*if ((ix%10) == 0)*/
                if (ddi_dma_sync((fobp)->fob_flash_hdl,
                                 2 * sizeof(struct fobnic_template),
                                 sizeof(struct fobnic_template),
                                 DDI_DMA_SYNC_FORCPU) != DDI_SUCCESS)
                {
                    mutex_exit(&fobp->fob_cvlock);
                    cmn_err(CE_WARN, "Could not sync flash TCB for CPU\n");
                    fob_miocnak(wq, mp, 0, EIO);
                    return;
                }
		if (tcbp->hdr.status != 0)
			break;
	}
	if (fobdebug)
		cmn_err(CE_CONT, "status = 0x%x, ix = %d\n", tcbp->hdr.status,
			ix);
        mutex_exit(&fobp->fob_cvlock);
	if (ix == 2000) {
            fob_miocnak(wq, mp, 0, EIO);
            cmn_err(CE_CONT, "flash data Xfer timeout\n");
            return;
	}
        fob_miocack(wq, mp, 0, 0);
}

/*
 * M_DATA "fastpath" info request.
 * Following the M_IOCTL mblk should come a DL_UNITDATA_REQ mblk.
 * We ack with an M_IOCACK pointing to the original DL_UNITDATA_REQ mblk
 * followed by an mblk containing the raw ethernet header corresponding
 * to the destination address.  Subsequently, we may receive M_DATA
 * msgs which start with this header and may send up
 * up M_DATA msgs with b_rptr pointing to a (uint32_t) group address
 * indicator followed by the network-layer data (IP packet header).
 * This is all selectable on a per-Stream basis.
 */
static void
fob_dl_ioc_hdr_info(wq, mp)
queue_t *wq;
mblk_t  *mp;
{
        mblk_t  *nmp;
        struct  fobstr   *slp;
        struct  fobdladdr        *dlap;
        dl_unitdata_req_t       *dludp;
        struct  ether_header    *headerp;
        struct  fob      *fobp;
        int     off, len;
        int     minsize;

        slp = (struct fobstr *)wq->q_ptr;
        minsize = sizeof (dl_unitdata_req_t) + RMX_ADDRL;
		if (fobdebug)
			cmn_err(CE_WARN,"fob_dl_ioc_hdr_info");
        /*
         * Sanity check the request.
         */
        if ((mp->b_cont == NULL) ||
                (MBLKL(mp->b_cont) < minsize) ||
                (*((uint32_t *)mp->b_cont->b_rptr) != DL_UNITDATA_REQ) ||
                ((fobp = slp->sl_fobp) == NULL)) {
                fob_miocnak(wq, mp, 0, EINVAL);
                return;
        }

        /*
         * Sanity check the DL_UNITDATA_REQ destination address
   	 * offset and length values.
         */
        dludp = (dl_unitdata_req_t *)mp->b_cont->b_rptr;
        off = dludp->dl_dest_addr_offset;
        len = dludp->dl_dest_addr_length;
        if (!MBLKIN(mp->b_cont, off, len) || (len != RMX_ADDRL)) {
                fob_miocnak(wq, mp, 0, EINVAL);
                return;
        }

        dlap = (struct fobdladdr *)(mp->b_cont->b_rptr + off);

        /*
         * Allocate a new mblk to hold the ether header.
         */
        if ((nmp = allocb(sizeof (struct ether_header), BPRI_MED)) == NULL) {
                fob_miocnak(wq, mp, 0, ENOMEM);
                return;
        }
        nmp->b_wptr += sizeof (struct ether_header);

        /*
         * Fill in the ether header.
         */
        headerp = (struct ether_header *)nmp->b_rptr;
        ether_copy(&dlap->dl_phys, &headerp->ether_dhost);
        ether_copy(&fobp->fob_ouraddr, &headerp->ether_shost);
        put_ether_type(headerp, slp->sl_sap);
		if (fobdebug)
			cmn_err(CE_WARN,"ethertype:0x%x",slp->sl_sap);

        /*
         * Link new mblk in after the "request" mblks.
         */
        linkb(mp, nmp);

        slp->sl_flags |= SLFAST;

        /*
         * XXX Don't bother calling fobsetipq() here.
         */

        fob_miocack(wq, mp, fob_msgsize(mp->b_cont), 0);
}


	/*	Generic DLPI interface routines */

static void
fobareq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;
	union	DL_primitives	*dlp;
	struct	fob	*fobp;
	int	ppa;

	slp = (struct fobstr *)wq->q_ptr;
	dlp = (union DL_primitives *)mp->b_rptr;

	if (fobdebug)
		cmn_err(CE_CONT,"fobareq\n");
	if (MBLKL(mp) < DL_ATTACH_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_ATTACH_REQ, DL_BADPRIM, 0);
		return;
	}

	if (slp->sl_state != DL_UNATTACHED) {
		if (fobdebug)
			cmn_err(CE_CONT,"fobareq: !DL_UNATTACHED\n");
		fobdlerrorack(wq, mp, DL_ATTACH_REQ, DL_OUTSTATE, 0);
		return;
	}

	ppa = dlp->attach_req.dl_ppa;
	if (fobdebug)
		cmn_err(CE_CONT,"ppa: %d\n", ppa);

	/*
	 * Valid ppa?
	 */
	for (fobp = fobdev; fobp; fobp = fobp->fob_nextp)
		if (ppa == ddi_get_instance(fobp->fob_dip))
			break;
	if (fobdebug)
		cmn_err(CE_CONT,"fobp = 0x%lx\n", fobp);
	if (fobp == NULL) {
		if (fobdebug)
			cmn_err(CE_CONT,"fobareq: DL_BADPPA: %d\n", ppa);
		fobdlerrorack(wq, mp, dlp->dl_primitive, DL_BADPPA, 0);
		return;
	}

	/*
	 * Has device been initialized?  Do so if necessary.
	 */
	if ((fobp->fob_flags & RMXRUNNING) == 0) {
		if (fobdebug) cmn_err(CE_CONT,"fobareq:running ?\n");
		if (fobinit(fobp)) {
			fobdlerrorack(wq, mp, dlp->dl_primitive,
				DL_INITFAILED, 0);
			return;
		}
	}

	/*
	 * Set link to device and update our state.
	 */
	slp->sl_fobp = fobp;
	slp->sl_state = DL_UNBOUND;

	fobdlokack(wq, mp, DL_ATTACH_REQ);
}

static void
fobdreq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;

	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_DETACH_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_DETACH_REQ, DL_BADPRIM, 0);
		return;
	}

	if (slp->sl_state != DL_UNBOUND) {
		fobdlerrorack(wq, mp, DL_DETACH_REQ, DL_OUTSTATE, 0);
		return;
	}

	fobdodetach(slp);
	fobdlokack(wq, mp, DL_DETACH_REQ);
}

/*
 * Detach a Stream from an interface.
 */
static void
fobdodetach(struct	fobstr	*slp)
{
	struct	fobstr	*tslp;
	struct	fob	*fobp;
	int	reinit = 0;
	int	ix;

	ASSERT(slp->sl_fobp);

	if (fobdebug)
		cmn_err(CE_WARN,"dodetach");
	fobp = slp->sl_fobp;
	slp->sl_fobp = NULL;

	/*
	 * Disable promiscuous mode if on. ???
	 */
	if (slp->sl_flags & SLALLPHYS) {
		slp->sl_flags &= ~SLALLPHYS;
		reinit = 1;
	}

	/*
	 * Disable ALLMULTI mode if on.
	 */
	if (slp->sl_flags & SLALLMULTI) {
		slp->sl_flags &= ~SLALLMULTI;
		reinit = 1;
	}

	/*
	 * Disable any Multicast Addresses.
	 */
        if (mcdebug)
            cmn_err(CE_WARN, "Clearing out multicast table!\n");
	if (slp->sl_mctab) {
		for (ix = 0; ix < slp->sl_mccount; ix++)
			slp->mcadd[ix]=0;
		kmem_free(slp->sl_mctab, RMXMCALLOC);
		slp->sl_mctab = NULL;
		reinit = 1;
	}
	slp->sl_mccount = 0;

	/*
	 * Detach from device structure.
	 * Uninit the device when no other streams are attached to it.
	 */
	for (tslp = fobstrup; tslp; tslp = tslp->sl_nextp)
		if (tslp->sl_fobp == fobp)
			break;
	if (tslp == NULL)
		fobuninit(fobp);
/*	else if (reinit)
		(void) fobinit(fobp);YYYYYYYY */

	slp->sl_state = DL_UNATTACHED;

	/* XXX from ae. */
	fobsetipq(fobp);	/* Why? */
}

static void
fobbreq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;
	union	DL_primitives	*dlp;
	struct	fob	*fobp;
	struct	fobdladdr	fobaddr;
	t_scalar_t	sap;
	/*uintptr_t	sap; XXX */
	int	xidtest;

	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_BIND_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_BIND_REQ, DL_BADPRIM, 0);
		return;
	}

	if (slp->sl_state != DL_UNBOUND) {
		fobdlerrorack(wq, mp, DL_BIND_REQ, DL_OUTSTATE, 0);
		return;
	}

	dlp = (union DL_primitives *)mp->b_rptr;
	fobp = slp->sl_fobp;
	sap = dlp->bind_req.dl_sap;
	xidtest = dlp->bind_req.dl_xidtest_flg;

	ASSERT(fobp);

	if (xidtest) {
		fobdlerrorack(wq, mp, DL_BIND_REQ, DL_NOAUTO, 0);
		return;
	}

	if (sap > ETHERTYPE_MAX) {
		fobdlerrorack(wq, mp, dlp->dl_primitive, DL_BADSAP, 0);
		return;
	}

	/*
	 * Save SAP value for this Stream and change state.
	 */
	slp->sl_sap = sap;
	slp->sl_state = DL_IDLE;

	fobaddr.dl_sap = sap;
	bcopy((caddr_t)&fobp->fob_ouraddr, (caddr_t)&fobaddr.dl_phys,
	sizeof (struct ether_addr));
	fobdlbindack(wq, mp, sap, (caddr_t)&fobaddr, RMX_ADDRL, 0, 0);
	/* XXX From ae */
	fobsetipq(fobp);	/* Why? */
}

static void
fobubreq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;

	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_UNBIND_REQ, DL_BADPRIM, 0);
		return;
	}

	if (slp->sl_state != DL_IDLE) {
		fobdlerrorack(wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0);
		return;
	}

	slp->sl_state = DL_UNBOUND;

	(void) putnextctl1(RD(wq), M_FLUSH, FLUSHRW);

	fobdlokack(wq, mp, DL_UNBIND_REQ);
	/* XXX from ae. */
	fobsetipq(slp->sl_fobp);
}

static void
fobireq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;
	dl_info_ack_t	*dlip;
	struct	fobdladdr	*dlap;
	struct	ether_addr	*ep;
	int	size;

	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_INFO_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_INFO_REQ, DL_BADPRIM, 0);
		return;
	}

	/*
	 * Exchange current msg for a DL_INFO_ACK.
	 */
	size = sizeof (dl_info_ack_t) + RMX_ADDRL + ETHER_ADDRL;
	if ((mp = fobexchange(wq, mp, size, M_PCPROTO, DL_INFO_ACK)) == NULL)
		return;

	/*
	 * Fill in the DL_INFO_ACK fields and reply.
	 */
	dlip = (dl_info_ack_t *)mp->b_rptr;
	*dlip = fobinfoack;
	dlip->dl_current_state = slp->sl_state;
	dlap = (struct fobdladdr *)(mp->b_rptr + dlip->dl_addr_offset);
	dlap->dl_sap = slp->sl_sap;
	if (slp->sl_fobp) {
	bcopy((caddr_t)&slp->sl_fobp->fob_ouraddr, (caddr_t)&dlap->dl_phys,
		sizeof (struct ether_addr));
	} else {
		bzero((caddr_t)&dlap->dl_phys, ETHER_ADDRL);
	}
	ep = (struct ether_addr *)(mp->b_rptr + dlip->dl_brdcst_addr_offset);
	bcopy((caddr_t)&etherbroadcastaddr, (caddr_t)ep,
		sizeof (struct ether_addr));

	qreply(wq, mp);
}

static void
fobponreq(queue_t *wq, mblk_t *mp)
{
	struct	fobstr	*slp;

	slp = (struct fobstr *)wq->q_ptr;

	if (fobdebug)
		cmn_err(CE_WARN,"fobponreq");

	if (MBLKL(mp) < DL_PROMISCON_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_PROMISCON_REQ, DL_BADPRIM, 0);
		return;
	}

	switch (((dl_promiscon_req_t *)mp->b_rptr)->dl_level) {
		case DL_PROMISC_PHYS:
			slp->sl_flags |= SLALLPHYS;
			break;

		case DL_PROMISC_SAP:
			slp->sl_flags |= SLALLSAP;
			break;

		case DL_PROMISC_MULTI:
			slp->sl_flags |= SLALLMULTI;
			break;

		default:
			fobdlerrorack(wq, mp, DL_PROMISCON_REQ,
				DL_NOTSUPPORTED, 0);
			return;
	}

	/*if (slp->sl_fobp) YYYYYYYYY
		(void) fobinit(slp->sl_fobp);*/
	if (slp->sl_fobp) {
            /*
             * This is not the corect way to implement this.
             * See hme driver.
             */
            if (slp->sl_flags&SLALLPHYS)
                slp->sl_fobp->fob_flags |= RMXPROMISC;
    }

	/* XXX from ae. */
	fobsetipq(slp->sl_fobp);

	fobdlokack(wq, mp, DL_PROMISCON_REQ);
}

static void
fobpoffreq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;
	int	flag;

	if (fobdebug)
		cmn_err(CE_WARN,"fobpoffreq");
	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_PROMISCOFF_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_BADPRIM, 0);
		return;
	}

	switch (((dl_promiscoff_req_t *)mp->b_rptr)->dl_level) {
		case DL_PROMISC_PHYS:
			flag = SLALLPHYS;
			break;

		case DL_PROMISC_SAP:
			flag = SLALLSAP;
			break;

		case DL_PROMISC_MULTI:
			flag = SLALLMULTI;
			break;

		default:
			fobdlerrorack(wq, mp, DL_PROMISCOFF_REQ,
				DL_NOTSUPPORTED, 0);
			return;
	}

	if ((slp->sl_flags & flag) == 0) {
		fobdlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_NOTENAB, 0);
		return;
	}

	slp->sl_flags &= ~flag;
	/*if (slp->sl_fobp)
		(void) fobinit(slp->sl_fobp);*/
    if (flag == SLALLPHYS) {
	    if (slp->sl_fobp)
            slp->sl_fobp->fob_flags &= ~RMXPROMISC;
    }


	/* XXX from ae */
	if (slp->sl_fobp)
		fobsetipq(slp->sl_fobp);

	fobdlokack(wq, mp, DL_PROMISCOFF_REQ);
}

/* Add multicast address. */
static void
fobemreq(queue_t *wq, mblk_t *mp)
{
	struct	fobstr	*slp;
	struct  fob *fobp;
	union	DL_primitives	*dlp;
	struct	ether_addr	*addrp;
	int	off;
	int	len, i;

	if (fobdebug)
		cmn_err(CE_WARN,"fobemreq");

	slp = (struct fobstr *)wq->q_ptr;
	/* get a pointer to our soft state structure. */
	fobp = slp->sl_fobp;	/* can it be NULL? */

	if (MBLKL(mp) < DL_ENABMULTI_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADPRIM, 0);
		return;
	}

	if (slp->sl_state == DL_UNATTACHED) {
		fobdlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_OUTSTATE, 0);
		return;
	}

	dlp = (union DL_primitives *)mp->b_rptr;
	len = dlp->enabmulti_req.dl_addr_length;
	off = dlp->enabmulti_req.dl_addr_offset;
	addrp = (struct ether_addr *)(mp->b_rptr + off);
	if (mcdebug)
		cmn_err(CE_CONT, "add multicast: %s\n", ether_sprintf(addrp));
	if ((len != ETHER_ADDRL) ||
		!MBLKIN(mp, off, len)) {
		fobdlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADADDR, 0);
		return;
	}

	if ((slp->sl_mccount + 1) >= RMXMAXMC) {
		fobdlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_TOOMANY, 0);
		return;
	}

	/*
	 * Allocate table on first request.
	 */
	if (slp->sl_mctab == NULL)
		if ((slp->sl_mctab = (struct ether_addr *)
			kmem_alloc(RMXMCALLOC, KM_NOSLEEP)) == NULL) {
			fobdlerrorack(wq, mp, DL_ENABMULTI_REQ,
				DL_SYSERR, ENOMEM);
			return;
			}
	/* Check to see if the address is already in the table. From ae */
        if (mcdebug)
            cmn_err(CE_CONT, "Checking table (%d entries)\n",slp->sl_mccount);
	for (i = 0;i < slp->sl_mccount; i++) {
            if (mcdebug)
                cmn_err(CE_CONT, "Entry %d: %s\n", i,
                        ether_sprintf(&slp->sl_mctab[i]));
		if(ether_cmp(&slp->sl_mctab[i], addrp) == 0) {
                    slp->mcadd[i]++;
                    if (mcdebug)
                    {
                        cmn_err(CE_CONT,
                                "rx: add mul %s already there: %d\n",
                                ether_sprintf(addrp),
                                slp->sl_mccount);
                    }
                    fobdlokack(wq, mp, DL_ENABMULTI_REQ);
                    return;
                } /* endif */
	} /* endfor */
	slp->mcadd[slp->sl_mccount]=1; /* first time added. */
	slp->sl_mctab[slp->sl_mccount++] = *addrp;
	/* MMMM */
        /* get mutex */
        mutex_enter(&fobp->fob_cvlock);
	/* set the header. No interrupt. */
	set_header((fobnic_msgh_t *)fobp->mcp, RXN_ADDRSET,
		   		sizeof (struct fobnic_addr));

	/* set the address type to "add multicast". */
	fobp->mcp->addr[0].addrType = RXN_MULTICAST_ADD;

	/* copy mc address to our mc structure. */
	bcopy((caddr_t)addrp, &fobp->mcp->addr[0].addr[0],
	       sizeof (struct ether_addr));

	/* flush cache */
   	(void) ddi_dma_sync((fobp)->fob_flash_hdl,
						sizeof(struct fobnic_template), 
						sizeof(struct fobnic_addr), DDI_DMA_SYNC_FORDEV);
	/* send the TCB. */
	send_tcb(fobp, fobp->mcbase);
	drv_usecwait(20000);
	/* invalidate cache. */
   	(void) ddi_dma_sync((fobp)->fob_flash_hdl,
						sizeof(struct fobnic_template), 
						sizeof(fobnic_msgh_t), DDI_DMA_SYNC_FORCPU);
	/* wait for the fw to complete. Use interrupt later. */
	i = 10;
	while(i > 0) {
		i--;
		if (fobp->mcp->hdr.status != 0)
			break;
		drv_usecwait(20000);
		/* invalidate cache. */
		(void) ddi_dma_sync((fobp)->fob_flash_hdl,
							sizeof(struct fobnic_template), 
							sizeof(fobnic_msgh_t), DDI_DMA_SYNC_FORCPU);
	}
	if (fobdebug)
		cmn_err(CE_CONT, "count = %d, add mcast status = 0x%x\n",
			slp->sl_mccount, fobp->mcp->hdr.status);
        mutex_exit(&fobp->fob_cvlock);
	fobdlokack(wq, mp, DL_ENABMULTI_REQ);
}

#if 0 /* Debug only. */
static void dump_mc_table(struct fobstr *slp, char *when)
{
    int i;

    cmn_err(CE_CONT, "MC table %s has %d entries:\n", when, slp->sl_mccount);
    for (i = 0; i < slp->sl_mccount; i++)
    {
        uchar_t *addrp;

        addrp = (uchar_t *)&(slp->sl_mctab[i].ether_addr_octet[0]);
        cmn_err(CE_CONT, "Address: %s  ADD = %d\n",
                ether_sprintf(addrp), slp->mcadd[i]);
    }
}
#endif

/* Delete multicast address. */
static void
fobdmreq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;
	union	DL_primitives	*dlp;
	struct	ether_addr	*addrp;
	struct  fob *fobp;
	int	off;
	int	len;
	int	i, ix;

	if (fobdebug)
		cmn_err(CE_WARN,"fobdmreq");
	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_DISABMULTI_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_DISABMULTI_REQ, DL_BADPRIM, 0);
		return;
	}

	if (slp->sl_state == DL_UNATTACHED) {
		fobdlerrorack(wq, mp, DL_DISABMULTI_REQ, DL_OUTSTATE, 0);
		return;
	}

	dlp = (union DL_primitives *)mp->b_rptr;
	len = dlp->disabmulti_req.dl_addr_length;
	off = dlp->disabmulti_req.dl_addr_offset;
	addrp = (struct ether_addr *)(mp->b_rptr + off);
	if (mcdebug)
		cmn_err(CE_CONT, "del multicast: %s\n", ether_sprintf(addrp));
                /*dump_mc_table(slp, "before");*/

	if ((len != ETHER_ADDRL) || !MBLKIN(mp, off, len)) {
		fobdlerrorack(wq, mp, DL_DISABMULTI_REQ, DL_BADADDR, 0);
		return;
	}

	/*
	 * Find the address in the multicast table for this Stream
	 * and delete it by shifting all subsequent multicast
	 * table entries over one.
	 */
	for (i = 0; i < slp->sl_mccount; i++)
		/* all this is architecture specific */
		if (bcmp((caddr_t)addrp, (caddr_t)&slp->sl_mctab[i],
			sizeof (struct ether_addr)) == 0)
		{
                    /*cmn_err(CE_CONT, "Address found, with count = %d\n",
                      slp->mcadd[i]);*/
			slp->mcadd[i]--;
			if (slp->mcadd[i]==0) { /* remove address. */
				bcopy((caddr_t)&slp->sl_mctab[i+1],
					(caddr_t)&slp->sl_mctab[i],
					((slp->sl_mccount - i) *
					sizeof (struct ether_addr)));
                                /* Need to shift the counters down too. */
                                bcopy((caddr_t)&slp->mcadd[i+1],
                                      (caddr_t)&slp->mcadd[i],
                                      (slp->sl_mccount - i) *sizeof(uint32_t));
				slp->sl_mccount--;
				/* MMMM. */
				/* get a pointer to our soft state structure. */
				fobp = slp->sl_fobp;	/* can it be NULL? */
                                /* get mutex */
                                mutex_enter(&fobp->fob_cvlock);
				/* set the header. No interrupt. */
				set_header((fobnic_msgh_t *)fobp->mcp, RXN_ADDRSET,
		   		   sizeof (struct fobnic_addr));
				/* set the address type to "del multicast". */
				fobp->mcp->addr[0].addrType = RXN_MULTICAST_DEL;
				/* copy mc address to our mc structure. */
				bcopy((caddr_t)addrp, &fobp->mcp->addr[0].addr[0],
			       sizeof (struct ether_addr));
				/* flush cache */
   				(void) ddi_dma_sync((fobp)->fob_flash_hdl,
									sizeof(struct fobnic_template), 
									sizeof(struct fobnic_addr),
									DDI_DMA_SYNC_FORDEV);
				/* send the TCB. */
				send_tcb(fobp, fobp->mcbase);
				drv_usecwait(40000);
				/* flush cache */
   				(void) ddi_dma_sync((fobp)->fob_flash_hdl,
									sizeof(struct fobnic_template), 
									sizeof(struct fobnic_addr),
									DDI_DMA_SYNC_FORCPU);
				ix= 10;
				while(ix > 0) {
					ix--;
					if (fobp->mcp->hdr.status != 0)
						break;
					drv_usecwait(20000);
					/* invalidate cache. */
					(void) ddi_dma_sync((fobp)->fob_flash_hdl,
										sizeof(struct fobnic_template), 
										sizeof(fobnic_msgh_t),
										DDI_DMA_SYNC_FORCPU);
				}
				if (fobdebug)
					cmn_err(CE_CONT, "del mcast status = 0x%x\n",
							fobp->mcp->hdr.status);
                                mutex_exit(&fobp->fob_cvlock);
				fobdlokack(wq, mp, DL_DISABMULTI_REQ);
                                /*dump_mc_table(slp, "after");*/
				return;
			} /* end of mcadd */
		}
	/* XXX from ae.
           cmn_err(CE_WARN, "Attempt to delete MC address - not found\n");
	fobdlerrorack(wq, mp, DL_DISABMULTI_REQ, DL_NOTENAB, 0); */
}

static void
fobpareq(queue_t	*wq, mblk_t	*mp)
{
	struct	fobstr	*slp;
	union	DL_primitives	*dlp;
	int	type;
	struct	fob	*fobp;
	struct	ether_addr	addr;

	if (fobdebug)
		cmn_err(CE_WARN,"fobpareq");

	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_PHYS_ADDR_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_BADPRIM, 0);
		return;
	}

	dlp = (union DL_primitives *)mp->b_rptr;
	type = dlp->physaddr_req.dl_addr_type;
	fobp = slp->sl_fobp;

	if (fobp == NULL) {
		fobdlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_OUTSTATE, 0);
		return;
	}

	switch (type) {
		case	DL_FACT_PHYS_ADDR:
            if (fobdebug)
                cmn_err(CE_WARN, "fobpareq: DL_FACT_PHYS_ADDR\n");
			(void) localetheraddr((struct ether_addr *)NULL, &addr);
			break;

		case	DL_CURR_PHYS_ADDR:
            if (fobdebug)
                cmn_err(CE_WARN, "fobpareq: DL_CURR_PHYS_ADDR\n");
			bcopy((caddr_t)&fobp->fob_ouraddr, (caddr_t)&addr,
				sizeof (struct ether_addr));
			break;

		default:
            if (fobdebug)
                cmn_err(CE_WARN, "fobpareq: default type 0x%x\n", type);
			fobdlerrorack(wq, mp, DL_PHYS_ADDR_REQ,
				DL_NOTSUPPORTED, 0);
			return;
	}

	fobdlphysaddrack(wq, mp, (caddr_t)&addr, ETHER_ADDRL);
}

/*
 * figure out your local ether address
 */
#if USEIT
static void
localetheraddr(struct ether_addr *hint, struct ether_addr * result)
{
	static int found = 0;
	static struct ether_addr addr;

	if (!found) {
		found = 1;
		if (hint == NULL)
				return;
		addr = *hint;
		if (fobdebug)
			cmn_err(CE_CONT, "?Ethernet address = %s\n",
		ether_sprintf(&addr));
	}
	if (result != NULL)
			*result = addr;
}
#endif


static void
fobspareq(queue_t *wq, mblk_t	*mp)
{
	struct	fobstr	*slp;
	union	DL_primitives	*dlp;
	int	off;
	int	len;
	struct	ether_addr	*addrp;
	struct	fob	*fobp;

	if (fobdebug)
		cmn_err(CE_WARN, "fobspareq");
	slp = (struct fobstr *)wq->q_ptr;

	if (MBLKL(mp) < DL_SET_PHYS_ADDR_REQ_SIZE) {
		fobdlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADPRIM, 0);
		return;
	}

	dlp = (union DL_primitives *)mp->b_rptr;
	len = dlp->set_physaddr_req.dl_addr_length;
	off = dlp->set_physaddr_req.dl_addr_offset;

	if (!MBLKIN(mp, off, len)) {
		fobdlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADPRIM, 0);
		return;
	}

	addrp = (struct ether_addr *)(mp->b_rptr + off);

	/*
	 * XXX if length of address isn't right or the address
	 * specified is a multicast or broadcast address.
	 * then return error.
	 */
	if ((len !=ETHER_ADDRL) ||
	   ((addrp->ether_addr_octet[0] & 01) == 1) ||
	   (ether_cmp(addrp, &etherbroadcastaddr) == 0)) {
	     fobdlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADADDR, 0);
	     return;
	}

	/*
	 * Error if this stream is not attached to a device.
	 */
	if ((fobp = slp->sl_fobp) == NULL) {
		fobdlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_OUTSTATE, 0);
		return;
	}

	/*
	 * Set new interface local address and re-init device.
	 * This is destructive to any other streams attached
	* to this device.
	 */
	bcopy((caddr_t)addrp, (caddr_t)&fobp->fob_ouraddr,
		sizeof (struct ether_addr));
/*	(void) fobinit(slp->sl_fobp); XXX */

	fobdlokack(wq, mp, DL_SET_PHYS_ADDR_REQ);
}

static void
fobudreq(queue_t *wq, mblk_t *mp)
{
	struct	 fobstr	*slp;
	register struct	fob *fobp;
	register dl_unitdata_req_t *dludp;
	mblk_t	 *nmp;
	struct	fobdladdr *dlap;
	struct	ether_header *headerp;
	t_uscalar_t	off, len;
	t_scalar_t sap;

	slp = (struct fobstr *)wq->q_ptr;
	fobp = slp->sl_fobp;

	if (fobdebug)
		cmn_err(CE_WARN,"fobudreq");
	/* XXX from ae */
	if (slp->sl_state != DL_IDLE) {
	if (fobdebug)
		fobdlerrorack(wq, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
		return;
	}

	dludp = (dl_unitdata_req_t *)mp->b_rptr;

	off = dludp->dl_dest_addr_offset;
	len = dludp->dl_dest_addr_length;

	/*
	 * Validate destination address format.
	 */
	if (!MBLKIN(mp, off, len) || (len != RMX_ADDRL)) {
		fobdluderrorind(wq, mp, (u_char *)(mp->b_rptr + off), len,
			DL_BADADDR, 0);
		return;
	}

	/*
	 * Error if no M_DATA follows.
	 */
	nmp = mp->b_cont;
	if (nmp == NULL) {
	if (fobdebug)
		cmn_err(CE_WARN,"nmp == NULL no M_DATA");
		fobdluderrorind(wq, mp, (u_char *)(mp->b_rptr + off), len,
			DL_BADDATA, 0);
		return;
	}

	dlap = (struct fobdladdr *)(mp->b_rptr + off);

	/*
	 * Create ethernet header by either prepending it onto the
	 * next mblk if possible, or reusing the M_PROTO block if not.
	 */
	if ((DB_REF(nmp) == 1) &&
		(MBLKHEAD(nmp) >= sizeof (struct ether_header)) &&
		(((uintptr_t)nmp->b_rptr & 0x1) == 0)) {
		nmp->b_rptr -= sizeof (struct ether_header);
		headerp = (struct ether_header *)nmp->b_rptr;
		bcopy((caddr_t)&dlap->dl_phys, (caddr_t)&headerp->ether_dhost,
			sizeof (struct ether_addr));
		bcopy((caddr_t)&fobp->fob_ouraddr,
			(caddr_t)&headerp->ether_shost,
			sizeof (struct ether_addr));

		headerp->ether_type = dlap->dl_sap;

		/* XXX */
		sap = dlap->dl_sap;
                sap = slp->sl_sap; /* DBC */
		freeb(mp);
		mp = nmp;
	} else {
		DB_TYPE(mp) = M_DATA;
		headerp = (struct ether_header *)mp->b_rptr;
		mp->b_wptr = mp->b_rptr + sizeof (struct ether_header);
		bcopy((caddr_t)&dlap->dl_phys, (caddr_t)&headerp->ether_dhost,
			sizeof (struct ether_addr));
		bcopy((caddr_t)&fobp->fob_ouraddr,
			(caddr_t)&headerp->ether_shost,
			sizeof (struct ether_addr));

		headerp->ether_type = dlap->dl_sap;

		sap = dlap->dl_sap;
                sap = slp->sl_sap; /* DBC */
	}


	/* XXX from ae. */
	/*
         * sap value in the range [0-1500] are treated equivalent
         * and represent a desire to use 802.3 mode. Therefore compute
         * the length and put it in the type field in header
         */

	if ((sap <= ETHER_MTU) || (slp->sl_sap == 0)) {
	if (fobdebug)
		cmn_err(CE_WARN,"###if sap");
	     sap = fob_msgsize(mp) - sizeof (struct ether_header);
	     put_ether_type(headerp, sap);
	}
	else {
	     put_ether_type(headerp, sap);
	}
	if (fobdebug)
		cmn_err(CE_WARN,"hearderp->ether_type:0x%x", headerp->ether_type);

	/*
	 * Transmit it.
	 */
	(void) fobstart(wq, mp, fobp);
}

/*
 * fobmask_interrupt: mask interrupt depending on the board.
 * parameters: pointer to state structure *.
 */
static void
fobmask_interrupt(register struct fob *fobp)
{
	switch(fobp->boardid) {
	case 675:
		/* mask outboung register interrupt */
		ddi_put32(fobp->fob_handle,
		 	  (uint32_t *)&fobp->fob_regsp[i21555_OUTBOUND_INT_MASK_REG/4],
			  i21555_OUTBOUND_INT_MASK);
		break;
    case 696:
		/* mask outboung register interrupt */
		ddi_put32(fobp->fob_handle,
		 	  (uint32_t *)&fobp->fob_regsp[i21555_OUTBOUND_INT_MASK_REG/4],
			  0x08000000);
        break;

	case i960:
		RMX_CSR_WRITE(fobp->fob_regsp[INT_MASK_REG/4], 0xff);
		break;
	}
}

/*
 * fobunmask_interrupt: unmask interrupt depending on the board.
 * parameters: pointer to state structure *.
 */
static void
fobunmask_interrupt(register struct fob *fobp)
{
	switch(fobp->boardid) {
	case 675:
		/* mask outboung register interrupt */
		ddi_put32(fobp->fob_handle,
		 	  (uint32_t *)&fobp->fob_regsp[i21555_OUTBOUND_INT_MASK_REG/4], 0);
		break;
    case 696:
		/* unmask outbound register interrupt */
		ddi_put32(fobp->fob_handle,
		 	  (uint32_t *)&fobp->fob_regsp[i21555_OUTBOUND_INT_MASK_REG/4], 
              ~0x08000000);
        break;

	case i960:
		RMX_CSR_WRITE(fobp->fob_regsp[INT_MASK_REG/4], ~8);
		break;
	}
}
/*
 * fobstart: Start transmission.
 * Return zero on success,
 * otherwise put msg on wq, set 'want' flag and return nonzero.
 */
int txDebug=0;
static int
fobstart(queue_t *wq, register mblk_t *mp, register struct fob *fobp)
{
    volatile struct  fobnic_pkt *tmdp, *ntmdp, *rmdp;
    struct   fobbuf   *lbp;
    mblk_t   *nmp = NULL;
    int      len;
    uintptr_t a;
    uintptr_t sbound, soff;		/* to copy on a align buffer. */
    uint8_t  *protocol;		/* pointer to  IP protocol. */
    uint16_t ethertype;		/* Ether type. */
    uint8_t  *ep;
    uint32_t *igmpp;
    uint32_t igmp=0;		/* igmp flag. */
    uint32_t value;
    int     i, ix;
    int     flags;
    int     donothing;
    struct ether_header *ehp;

	if (fobdebug)
		cmn_err(CE_WARN,"in fobstart %d",fobp->fob_bufi);
	if (txDebug)
		cmn_err(CE_WARN,"in fobstart %d",fobp->tx_queued);
	/*
	 * I would think this is not the correct place to put
	 * this becasue the packet may not be sent.
 	 * update MIB II statistics.
	 */
	ehp = (struct ether_header *)mp->b_rptr;
	BUMP_OutNUcast(fobp, ehp);

	mutex_enter(&fobp->fob_xmitlock);
	/* give first RX TCB to  IOCP. Just one to start up. */
	if ((fobp->fob_flags & DESC_INIT) == 0) {
		int count;

        /* 66x board get 1, 675 and 696 board send down all
         * of the receive descriptors 
         */
		if (fobp->boardid == 675 || fobp->boardid == 696)
			count = fobp->fob_nrmds;
		else
			count = 1;
		mutex_enter(&fobp->fob_intrlock);
		fobp->fob_last = rmdp = fobp->fob_rmdp;
		ix = (int)(fobp->fob_last - fobp->fob_rmdp);
		fobp->postrcv[ix][0]++;	/* this TCB is posted */
		for (ix = 0; ix < count; ix++) {
			rmdp->nsegs=1;	/* get overwrited by the length. */
			rmdp->dir = RECEIVE;
			set_header((fobnic_msgh_t *)rmdp, RXN_PKT,
	   		   		sizeof(struct fobnic_pkt)|1);
			RMXSYNCIOPB(fobp, rmdp, sizeof (struct fobnic_pkt),
		    	    	    DDI_DMA_SYNC_FORDEV);
			send_tcb(fobp, RMXIOPBIOADDR(fobp, rmdp));
			/* Move to the next available TCB */
			fobp->fob_last = rmdp = NEXTRMD(fobp, rmdp);
			if (ix == 0)
				fobp->fob_queued=1;
			else
				fobp->fob_queued++;
		}
		fobp->fob_flags |= DESC_INIT;
		mutex_exit(&fobp->fob_intrlock);
		/* unmask interrupt */
		fobunmask_interrupt(fobp);
	}
#define TX_INTS_ENABLED
#ifndef TX_INTS_ENABLED
	/* reclaiming some TMDs. */
	/*if (fobp->tx_queued > 32) {
		mutex_enter(&fobp->fob_xmitlock);
		fobreclaim(fobp);
		mutex_exit(&fobp->fob_xmitlock);
	}*/
	 if (fobp->fob_tnextp > fobp->fob_tcurp) {
     	i = fobp->fob_tnextp - fobp->fob_tcurp;
        if ((fobp->fob_tnextp - fobp->fob_tcurp) > 8)
        	fobreclaim(fobp);
     } else {
     	i = fobp->fob_tcurp - fobp->fob_tnextp;
        if (i && (i < (56)))
        	fobreclaim(fobp);
     }
#endif

	mutex_exit(&fobp->fob_xmitlock);

	flags = fobp->fob_flags;
	if (fobdebug)
		cmn_err(CE_CONT, "fobstart:fobp->fob_flags = 0x%x\n", fobp->