/*************************************************************************** * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KSTAT #ifdef KSTAT #include #endif #include #include #include #include #include #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=" */ #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->