📄 if_sonic.c
字号:
/* $Id: if_sonic.c,v 1.6 1999/03/29 12:23:24 chris Exp $ *//* * if_sonic.c: National Semiconductor SONIC Driver * * Copyright (c) 1992 ALGORITHMICS LIMITED * ALL RIGHTS RESERVED * * THIS SOFTWARE PRODUCT CONTAINS THE UNPUBLISHED SOURCE * CODE OF ALGORITHMICS LIMITED * * The copyright notices above do not evidence any actual * or intended publication of such source code. * */#ifndef INET#define INET#endif#include "sys/param.h"#include "sys/systm.h"#include "sys/mbuf.h"#include "sys/protosw.h"#include "sys/socket.h"#include "sys/errno.h"#include "sys/uio.h"#include "sys/ioctl.h"#include "sys/syslog.h"#include "net/if.h"#include "net/if_types.h"#include "net/netisr.h"#include "net/route.h"#ifdef INET#include "netinet/in.h"#include "netinet/in_systm.h"#include "netinet/in_var.h"#include "netinet/ip.h"#include "netinet/if_ether.h"#endif#include "mips.h"#include "sbd.h"#include "if_sonic.h"#define NSONIC 1 /* number of sonics */#define TINYFRAG 12 /* tx frags < this size count as "tiny" */#define MAXTINIES 3 /* max "tiny" frags, after which we compress */#define SNRETRIES 5 /* max retries on fatal tx errors */#define DBG_TXPKT 0x02#define DBG_RXPKT 0x04int T_sn;#define T_txp (T_sn & DBG_TXPKT)#define T_rxp (T_sn & DBG_RXPKT)#ifdef R4000#define clean_dcache mips_clean_dcache#else#define clean_dcache r3k_dclean#endif/* * Statistics collected over time * (same order as enpstats) */struct sn_stats { int ss_tpacks; /* transmit ok */ int ss_tmore; /* transmit more than one retry */ int ss_tone; /* transmit one retry */ int ss_cerr; /* transmit failed after retries */ int ss_tdef; /* transmit deferred */ int ss_tbuff; /* transmit buffer errors */ int ss_tuflo; /* transmit silo underflow */ int ss_tlcol; /* transmit late collisions */ int ss_tlcar; /* transmit lost carriers */ int ss_babl; /* transmit babbling */ int ss_noheart; /* transmit no heartbeat */ int ss_tmerr; /* transmit memory error */ int ss_rpacks; /* receive ok */ int ss_miss; /* receive missed packets */ int ss_rcrc; /* receive crc errors */ int ss_rfram; /* receive framing errors */ int ss_rbuff; /* receive buffer errors */ int ss_roflo; /* receive silo overflow */ int ss_rmerr; /* receive memory error */};static struct sn_softc { struct arpcom sn_ac;#define sn_if sn_ac.ac_if /* network visible interface */#define sn_enaddr sn_ac.ac_enaddr /* hardware ethernet address */ volatile struct sn_reg *sn_csr; /* hardware pointer */ int sn_rxmark; /* index in rx ring */ int sn_rramark; /* index into rra of wp */ volatile struct RXpkt *sn_lrxp; /* last RDA available to chip */ int sn_retries; /* transmit retries */ int sn_rbe; /* had an RBE signal */ struct sn_stats sn_sum; /* software maintained stats */ u_int sn_crct; /* crc tally from chip */ u_int sn_faet; /* frame error tally from chip */ u_int sn_mpt; /* missed packet tally from chip */ int sn_rev; /* chip revision number */} sn_softc[NSONIC];/* Public functions from outside this module */static int snifinit (int unit);static int snioctl (struct ifnet *ifp, int cmd, caddr_t data);static int snifstart (struct ifnet *ifp);static int snwatch (int unit);static int snreset (int unit);static void snintr (int unit);/* Local functions */static int snrestart (struct sn_softc *sn);static int snstartup (struct sn_softc *sn);static int snclosedown (struct sn_softc *sn);static int snput (struct sn_softc *sn, struct mbuf *m0);static void sntxint (struct sn_softc *);static void sntxdump (volatile struct TXpkt *txp);static void snrxint (struct sn_softc *);static int snerrint (struct sn_softc *, u_int isr);static struct mbuf *snget (struct sn_softc *sn, caddr_t addr, int dlen, int toff, int tlen);static int snread (struct sn_softc *, volatile struct RXpkt *);static struct mbuf *sn_fillup (struct mbuf *m0, int minlen);static void caminitialise (void);static void camentry (int, u_char *ea);static int camprogram (struct sn_softc *);static int allocatebuffers (void);static void initialise_tda (struct sn_softc *);static void initialise_rda (struct sn_softc *);static void initialise_rra (struct sn_softc *);#ifdef MIPSELstatic void snswapb (u_char *s, u_char *d, int len);static struct mbuf *snswapm (struct mbuf *m);#endif#if defined(XDSSONICBUG)/* * SONIC buffers need to be aligned 64 bit aligned. * These macros calculate and verify alignment. */#define SONICDW 64#define SONICALIGN (SONICDW/16)#else/* * SONIC buffers need to be aligned 16 or 32 bit aligned. * These macros calculate and verify alignment. */#define SONICDW 32#define SONICALIGN (SONICDW/8)#endif#define UPPER(x) (K0_TO_PHYS((x)) >> 16)#define LOWER(x) (((unsigned int)(x)) & 0xffff)/* * buffer sizes in 32 bit mode * 1 TXpkt is 4 hdr words + (3 * FRAGMAX) + 1 link word * FRAGMAX == 16 => 54 words == 216 bytes * * 1 RxPkt is 7 words == 28 bytes * 1 Rda is 4 words == 16 bytes */#define NRRA 32 /* # receive resource descriptors */#define RRAMASK (NRRA-1) /* why it must be power of two */#define NRBA 16 /* # receive buffers < NRRA */#define NRDA NRBA /* # receive descriptors */#define NTDA 4 /* # transmit descriptors */#define CDASIZE sizeof(struct CDA)#define RRASIZE (NRRA*sizeof(struct RXrsrc))#define RDASIZE (NRDA*sizeof(struct RXpkt))#define TDASIZE (NTDA*sizeof(struct TXpkt))/* size of FCS (CRC) appended to received packets */#define FCSSIZE 4 /* buffer size (enough for 1 max packet 1520 up to cache line boundary) */#ifdef XDSCONICBUG#define RBASIZE (1536*2)#else#define RBASIZE 1536#endif#ifdef XDS/* * transmit data must be copied into SRAM */#define TBASIZE RBASIZE#endif/* eobc set for only one packet per buffer */#define EOBC 1520 /* Assumes 32 bit operation *//* total buffer size needed for all descriptors */#define SONICBUFSIZE ((RRASIZE+CDASIZE+RDASIZE+TDASIZE)*2 + SONICALIGN - 1)/* * aligned pointers into sonic buffers */static volatile struct RXrsrc *rra; /* receiver resource descriptors */static volatile struct RXpkt *rda; /* receiver desriptors */static volatile struct TXpkt *tda; /* transmitter descriptors */static volatile struct CDA *cda; /* CAM descriptors *//* * receive buffers for sonic accessed by SONIC * each buffer will hold one ethernet packet */static char *rba;#ifdef XDSstatic char *tba;#endif/* Meta transmit descriptors */static struct mtd { struct mtd *mtd_link; volatile struct TXpkt *mtd_txp; struct mbuf *mtd_mbuf;#ifdef XDS char *mtd_tba;#endif} mtda[NTDA];static struct mtd *mtdfree; /* list of free meta transmit descriptors */static struct mtd *mtdhead; /* head of descriptors assigned to chip */static struct mtd *mtdtail; /* tail of descriptors assigned to chip */static struct mtd *mtdnext; /* next descriptor to give to chip */static struct mtd *mtd_alloc (void);static void mtd_free (struct mtd *);static void sntxdone (struct sn_softc *, struct mtd *);/* * eninit(): initialise ethernet * check to see if sonic chip is on the machine */inteninit (){ int unit = 0; struct sn_softc *sn = &sn_softc[unit]; struct ifnet *ifp = &sn->sn_if; volatile struct sn_reg *csr; int timeout; u_short dcr; csr = (struct sn_reg *) PHYS_TO_K1 (SONIC_BASE); /* reset Sonic chip */#if defined(XDS) *(u_int *) PHYS_TO_K1 (VME_SONIC_RES) = RESET_ACTIVE; wbflush (); DELAY(1000); *(u_int *) PHYS_TO_K1 (VME_SONIC_RES) = RESET_INACTIVE; wbflush ();#elif defined(P4000) *(u_int *) PHYS_TO_K1 (NET_RESET_) = RESET_ZERO; wbflush(); DELAY(1000); *(u_int *) PHYS_TO_K1 (NET_RESET_) = RESET_ONE; wbflush();#else /* other Algorithmics boards */ *(u_int *) PHYS_TO_K1 (BCRR) &= ~BCRR_ETH; wbflush(); DELAY(1000); *(u_int *) PHYS_TO_K1 (BCRR) |= BCRR_ETH; wbflush();#endif DELAY(1000); if (!(csr->s_cr & CR_RST)) { log (LOG_ERR, "sonic: did not reset\n"); return; } /* config it */#if defined(XDS) dcr = DCR_ASYNC|DCR_WAIT1|DCR_DW32|DCR_DMABLOCK|DCR_RFT24|DCR_TFT24;#else dcr = DCR_ASYNC|DCR_WAIT0|DCR_DW32|DCR_DMABLOCK|DCR_RFT24|DCR_TFT24;#endif csr->s_dcr = dcr; wbflush(); if ((csr->s_dcr & ~(DCR_USR1|DCR_USR0)) != dcr) { log (LOG_ERR, "sonic: cannot configure\n"); return; } csr->s_imr = 0; wbflush(); sn->sn_rev = csr->s_sr; ifp = &sn->sn_if; ifp->if_name = "en"; ifp->if_unit = unit; if (sbdethaddr (sn->sn_enaddr) < 0) return; log (LOG_INFO, "%s%d: rev %d, ethernet address: %s\n", sn->sn_if.if_name, sn->sn_if.if_unit, sn->sn_rev, ether_sprintf (sn->sn_enaddr)); /* network management */ ifp->if_type = IFT_ETHER; ifp->if_addrlen = 6; ifp->if_hdrlen = 14; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;#ifdef DEBUG ifp->if_flags |= IFF_DEBUG;#endif ifp->if_init = snifinit; ifp->if_output = ether_output; ifp->if_start = snifstart; ifp->if_ioctl = snioctl; ifp->if_watchdog = snwatch; ifp->if_reset = snreset; ifp->if_timer = 0; sn->sn_csr = csr; if_attach (ifp);#ifdef PROM if_newaddr(ifp, IFT_ETHER, (caddr_t)((struct arpcom *)ifp)->ac_enaddr);#else#if defined(P4000) && defined(USEINTS) sbd_setvec (CR_HINT0, IRR(0,IRR0_NET), snintr, unit);#endif#endif}/* * snifinit: initialise interface. */static intsnifinit (unit) int unit;{#ifdef notdef /* let SIOCSIFADDR/FLAGS do the job */ if (unit < NSONIC) { register struct sn_softc *sn = &sn_softc[unit]; int s = splimp (); if (snstartup (sn) == 0) sn->sn_if.if_flags |= IFF_UP; (void) splx (s); }#endif}/* * snioctl: process an ioctl request. */static intsnioctl (struct ifnet *ifp, int cmd, caddr_t data){ struct ifaddr *ifa = (struct ifaddr *)data; struct ifreq *ifr = (struct ifreq *)data; struct sn_softc *sn = &sn_softc[ifp->if_unit]; char *bp; int error = 0; int s = splimp(); switch (cmd) { case SIOCPOLL: if (ifp->if_flags & IFF_RUNNING) snintr (ifp->if_unit); break; case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) {#ifdef INET case AF_INET: if (!(error = snstartup (sn))) { ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;#ifdef PROM if (IA_SIN(ifa)->sin_addr.s_addr != INADDR_ANY && (IA_SIN(ifa)->sin_addr.s_addr >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET)#endif arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); } break;#endif INET#ifdef NS case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *)(ns->ns_addr); else { /* force reset of controller for new address */ snclosedown (sn); bcopy((caddr_t)ina->x_host.c_host, (caddr_t)ns->ns_addr, sizeof(ns->ns_addr)); } error = snstartup (sn); break; }#endif default: error = snstartup (sn); break; } break; case SIOCSIFFLAGS: if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_UP) /* UP switched on, but not RUNNING: start up interface */ error = snstartup (sn); else if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_RUNNING) /* UP switched off, but still RUNNING: close down interface */ snclosedown (sn); break; default: error = EINVAL; } splx (s); return (error);}static intsnreset (int unit){ struct sn_softc *sn = &sn_softc[unit]; if (sn->sn_if.if_flags & IFF_UP) snrestart (sn);}/* * snrestart(): reset and restart the SONIC * * Called in case of fatal hardware/software errors. */static intsnrestart (struct sn_softc *sn){ int isup = sn->sn_if.if_flags & IFF_UP; int error; snclosedown(sn); error = snstartup(sn); if (error == 0 && isup) { /* restart dequeing of tx packets */ sn->sn_if.if_flags |= IFF_UP; snifstart (&sn->sn_if); } return error;}static intsnstartup (struct sn_softc *sn){ volatile struct sn_reg *csr = sn->sn_csr; u_short rcr; int error, s; if (!csr) /* no such device */ return ENXIO; if (!sn->sn_if.if_addrlist) /* no addresses */ return EINVAL; if (sn->sn_if.if_flags & IFF_RUNNING) /* already running */ return (0); s = splhigh ();#if 0 /* unreset it */#if defined(XDS) *(u_int *) PHYS_TO_K1 (VME_SONIC_RES) = RESET_INACTIVE; wbflush ();#elif defined(P4000) *(u_int *) PHYS_TO_K1 (NET_RESET_) = RESET_ONE; wbflush();#else *(u_int *) PHYS_TO_K1 (BCRR) |= BCRR_ETH; wbflush();#endif DELAY(1000);#endif if (!(csr->s_cr & CR_RST)) { error = EIO; goto bad; } log (LOG_INFO, "%s%d: starting interface\n", sn->sn_if.if_name, sn->sn_if.if_unit); /* config it */#if defined(XDS) csr->s_dcr = DCR_ASYNC|DCR_WAIT1|DCR_DW32|DCR_DMABLOCK|DCR_RFT24|DCR_TFT16;#elif defined(P4000) csr->s_dcr = DCR_EXBUS|DCR_LBR| DCR_ASYNC|DCR_WAIT0|DCR_DW32|DCR_DMABLOCK|DCR_RFT24|DCR_TFT16;#else csr->s_dcr = DCR_ASYNC|DCR_WAIT0|DCR_DW32|DCR_DMABLOCK|DCR_RFT24|DCR_TFT16;#endif csr->s_dcr2 = 0; wbflush (); rcr = RCR_BRD|RCR_LBNONE; if (sn->sn_if.if_flags & IFF_PROMISC) rcr |= RCR_PRO; csr->s_rcr = rcr; /* set interrupt mask */ csr->s_imr = IMR_BREN | IMR_PTXEN | IMR_TXEREN | IMR_HBLEN | IMR_PRXEN | IMR_RDEEN | IMR_RBAEEN | IMR_RFOEN | IMR_CRCEN | IMR_FAEEN | IMR_MPEN; /* clear pending interrupts */ csr->s_isr = ~0; /* clear tally counters */ csr->s_crct = ~0; csr->s_faet = ~0; csr->s_mpt = ~0; /* initialise memory descriptors */ if (rba) { bzero (rba, NRBA * RBASIZE); if (IS_K0SEG (rba)) clean_dcache (rba, NRBA * RBASIZE); } else { error = allocatebuffers (); if (error) goto bad; } caminitialise (); initialise_tda (sn); initialise_rda (sn); initialise_rra (sn); /* enable (unreset) the chip */ csr->s_cr = 0; wbflush(); /* program the cam with our address (m/c addresses already present) */ camentry (0, sn->sn_enaddr); if (!camprogram (sn)) goto bad; /* get it to read initial resource descriptors */ csr->s_cr = CR_RRRA; wbflush (); while (csr->s_cr & CR_RRRA) DELAY(100); /* enable receiver */ csr->s_cr = CR_RXEN; wbflush (); /* flag interface as "running" */ sn->sn_if.if_flags |= IFF_RUNNING; sn->sn_retries = 0; sn->sn_rbe = 0; splx(s); return 0; bad: snclosedown (sn); return error;}/* * snclosedown(): close down an interface and free its buffers * Called on final close of device, or if snstartup() fails * part way through. */static intsnclosedown (struct sn_softc *sn){ register volatile struct sn_reg *csr = sn->sn_csr; int s = splhigh(); sn->sn_if.if_flags &= ~(IFF_RUNNING | IFF_UP | IFF_OACTIVE); sn->sn_if.if_timer = 0; log (LOG_INFO, "%s%d: stopping interface\n", sn->sn_if.if_name, sn->sn_if.if_unit); /* update tally counters */ sn->sn_crct += csr->s_crct; sn->sn_faet += csr->s_faet; sn->sn_mpt += csr->s_mpt; /* forcibly shut the chip up */ csr->s_cr = CR_RST; wbflush(); DELAY(1000); /* free all receive buffers (currently static so nowt to do) */ /* free all transmit mbufs still pending */ { struct mtd *mtd; for (mtd = mtdhead; mtd; mtd = mtd->mtd_link) { if (mtd->mtd_mbuf) { m_freem (mtd->mtd_mbuf); mtd->mtd_mbuf = 0; } } } splx(s); return 0;}#ifdef PROMstatic intsncheckclient (struct sn_softc *sn){ register volatile struct sn_reg *csr = sn->sn_csr; if (csr->s_urra != UPPER (rra) || csr->s_rsa != LOWER (rra)) { /* client has reprogrammed sonic, we must now ignore it */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -