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, ðerbroadcastaddr) == 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 *)®sp, 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)ðerbroadcastaddr, (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, ðerbroadcastaddr) == 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->