📄 if_xdssonic.c
字号:
/* 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);/* sonicdump ("After reading resource descriptors", csr); */ /* 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; unsigned short upper = csr->s_urra; unsigned short lower = csr->s_rsa; static int consecutivefails = 0; if (upper != UPPER (rra) || lower != LOWER (rra)) { /* client has reprogrammed sonic, we must now ignore it */ sn->sn_if.if_flags &= ~IFF_RUNNING; consecutivefails++; log (LOG_DEBUG, "%s%d: count %d: 0x%04x/0x%04x 0x%04x/0x%04x\n", sn->sn_if.if_name, sn->sn_if.if_unit, consecutivefails, upper, UPPER (rra), lower, LOWER (rra)); if (consecutivefails <= 3) return 1; log (LOG_DEBUG, "%s%d: reprogrammed by client, PMON ignoring\n", sn->sn_if.if_name, sn->sn_if.if_unit); return 0; } consecutivefails = 0; return 1;}#endifstatic intsnifstart (struct ifnet *ifp){ register struct sn_softc *sn = &sn_softc[ifp->if_unit]; int s;#ifdef PROM if (!sncheckclient (sn)) return ENETDOWN;#endif /* * start feeding any queued packets to chip */ s = splimp (); for (;;) { struct mbuf *m; IF_DEQUEUE (&ifp->if_snd, m); if (m == 0) /* nothing left to send */ break; if (!snput (sn, m)) { /* not enough space */ IF_PREPEND (&ifp->if_snd, m); ifp->if_flags |= IFF_OACTIVE; ifp->if_oerrors++; break; } } (void) splx (s); return 0;}/* * stuff packet into sonic (at splimp) */#if defined(XDS)static intsnput (struct sn_softc *sn, struct mbuf *m0){ volatile struct sn_reg *csr = sn->sn_csr; register volatile union frag *fr; register struct mtd *mtdnew; register volatile struct TXpkt *txp; register struct mbuf *m; int len, i; unsigned char *ptba; unsigned int pa; if (!csr) return 0; /* grab the replacement mtd */ if ((mtdnew = mtd_alloc()) == 0) return 0; /* this packet goes in mtdnext */ txp = mtdnext->mtd_txp; txp->config = 0; txp->status = 0;#ifdef MIPSEL /* Byte-swap the mbuf chain */ m0 = snswapm (m0);#endif /* copy the data into the static RAM buffer */ fr = &txp->frags[0]; ptba = mtdnext->mtd_tba; len = 0; for (m = m0; m; m = m->m_next) { copy_host_to_data (mtod (m, char *), ptba, m->m_len); ptba += m->m_len; len += m->m_len; } /* pad out last fragment for minimum size */ if (len < ETHERMIN + sizeof(struct ether_header)) len = ETHERMIN + sizeof(struct ether_header);#ifdef XDSSONICBUG xds_expand_buffer (mtdnext->mtd_tba, len);#else#if defined(R4000) /* flush data to static RAM */ clean_dcache(mtdnext->mtd_tba, len);#endif#endif pa = K0_TO_PHYS(mtdnext->mtd_tba); txp->frag_count = 1; txp->pkt_size = len; fr->frag_ptrlo = LOWER(pa); fr->frag_ptrhi = UPPER(pa); fr->frag_size = len; /* link onto the next mtd that will be used */ fr++; fr->tlink = LOWER(mtdnew->mtd_txp) | EOL; if (!mtdhead) { /* no current transmit list, so start with this one */ mtdhead = mtdnext; } else { /* have an existing transmit list, so append it to end of list * note mtdnext is already physically linked to mtdtail in * mtdtail->mtd_txp->frags[mtdtail->mtd_txp->frag_count].tlink */ mtdtail->mtd_txp->frags[mtdtail->mtd_txp->frag_count].tlink &= ~EOL; wbflush (); }/* sonictxdump ("Transmit descriptor", txp); *//* sonicdump ("Before kicking transmitter", csr); */ /* kick transmitter */ csr->s_cr = CR_TXP; wbflush ();/* DELAY(1000); *//* sonicdump ("After kicking transmitter\n", csr); */ /* update s/w linkage */ mtdnext->mtd_mbuf = m0; /* to be freed on txint */ mtdnext->mtd_link = mtdnew; mtdtail = mtdnext; mtdnext = mtdnew; sn->sn_if.if_timer = 5; /* 5 second watchdog */ return 1;}#elsestatic intsnput (struct sn_softc *sn, struct mbuf *m0){ volatile struct sn_reg *csr = sn->sn_csr; register volatile union frag *fr; register struct mtd *mtdnew; register volatile struct TXpkt *txp; register struct mbuf *m; int firstattempt = 1; int len, nfr, i, tinyfrags, needcompress; if (!csr) return 0; /* grab the replacement mtd */ if ((mtdnew = mtd_alloc()) == 0) return 0; /* this packet goes in mtdnext */ txp = mtdnext->mtd_txp; txp->config = 0; txp->status = 0; retry: /* see whether it will fit in the TDA */ tinyfrags = needcompress = len = nfr = 0; fr = &txp->frags[0]; for (m = m0; m; m = m->m_next) { unsigned va = mtod (m, unsigned); int resid = m->m_len; len += resid; while (resid) { unsigned n = resid;#ifndef PROM if (IS_KSEG2 (va)) { /* this could cross a page boundary */ n = NBPC - (va & (NBPC - 1)); if (n > resid) n = resid; }#endif /* heuristic to prevent transmit underflow */ if (n < TINYFRAG) { if (++tinyfrags > MAXTINIES && tinyfrags > needcompress) needcompress = tinyfrags; /* remember maximum */ } else tinyfrags = 0; nfr++; va += n; resid -= n; } } if (needcompress != 0 || nfr > FRAGMAX) { if (firstattempt) { /* try to compress chain */ m0 = sn_fillup (m0, ETHERMTU / FRAGMAX + 1); firstattempt = 0; goto retry; } mtd_free (mtdnew); m_freem (m0); log (LOG_WARNING, "%s%d: frag compress failed (%d frags, %d tiny)\n", sn->sn_if.if_name, sn->sn_if.if_unit, nfr, needcompress); sn->sn_if.if_oerrors++; return 1; }#ifdef MIPSEL /* Byte-swap the mbuf chain */ m0 = snswapm (m0);#endif#ifdef R4000 /* hand mbuf data over to Sonic */ if (IS_K0SEG (m0)) for (m = m0; m; m = m->m_next) if (m->m_len) clean_dcache (mtod(m, caddr_t), m->m_len);#endif /* Now fill up the txd fragment list */ fr = &txp->frags[0]; for (m = m0; m; m = m->m_next) { unsigned va = mtod (m, unsigned); int resid = m->m_len; while (resid) { u_long pa; unsigned n = resid;#ifndef PROM pa = kvtophys (va); if (IS_KSEG2 (va)) { /* this could cross a page boundary */ n = NBPC - (pa & (NBPC - 1)); if (n > resid) n = resid; }#else pa = K1_TO_PHYS (va);#endif fr->frag_ptrlo = pa; fr->frag_ptrhi = pa >> 16; fr->frag_size = n; fr++; va += n; resid -= n; } } /* pad out last fragment for minimum size */ if (len < ETHERMIN + sizeof(struct ether_header)) { int pad = (ETHERMIN + sizeof(struct ether_header)) - len; (fr-1)->frag_size += pad; len = ETHERMIN + sizeof(struct ether_header); } txp->frag_count = nfr; txp->pkt_size = len; /* link onto the next mtd that will be used */ fr->tlink = LOWER(mtdnew->mtd_txp) | EOL; if (!mtdhead) { /* no current transmit list, so start with this one */ mtdhead = mtdnext; } else { /* have an existing transmit list, so append it to end of list * note mtdnext is already physically linked to mtdtail in * mtdtail->mtd_txp->frags[mtdtail->mtd_txp->frag_count].tlink */ mtdtail->mtd_txp->frags[mtdtail->mtd_txp->frag_count].tlink &= ~EOL; wbflush (); } /* kick transmitter */ csr->s_cr = CR_TXP; wbflush (); /* update s/w linkage */ mtdnext->mtd_mbuf = m0; /* to be freed on txint */ mtdnext->mtd_link = mtdnew; mtdtail = mtdnext; mtdnext = mtdnew; sn->sn_if.if_timer = 5; /* 5 second watchdog */ return 1;}#endifvoidsnintr (int unit){ struct sn_softc *sn = &sn_softc[unit]; volatile struct sn_reg *csr = sn->sn_csr; register u_int isr; if (unit >= NSONIC || !csr) return; while (isr = csr->s_isr) {/* printf ("snintr: csr=0x%x 0x%x\n", csr, isr); */#ifdef PROM if (!sncheckclient (sn)) return;#endif /* scrub the interrupts that we are going to service */ csr->s_isr = isr & ~ISR_RBE; wbflush (); if (isr & ISR_BR) { log (LOG_WARNING, "%s%d: bus error\n", sn->sn_if.if_name, sn->sn_if.if_unit); snrestart (sn); break; } if (isr & (ISR_LCD|ISR_PINT|ISR_TC)) log (LOG_WARNING, "%s%d: unexpected interrupt status 0x%x\n", sn->sn_if.if_name, sn->sn_if.if_unit, isr); if (isr & (ISR_TXDN|ISR_TXER)) sntxint (sn); if (isr & ISR_PKTRX) snrxint (sn); if (isr & ISR_ERRS) { if (snerrint (sn, isr)) break; } else { sn->sn_rbe = 0; } }}/* * Error interrupt routine */static intsnerrint (struct sn_softc *sn, u_int isr){ if (isr & ISR_RBE) { sn->sn_if.if_ierrors++; sn->sn_sum.ss_rbuff++; log (LOG_INFO, "%s%d: receive buffer exhausted, isr=0x%x\n", sn->sn_if.if_name, sn->sn_if.if_unit, isr); if (!sn->sn_rbe || (isr & ISR_PKTRX)) { /* probably managed to free some rx space, try again */ sn->sn_csr->s_isr = ISR_RBE; wbflush (); sn->sn_rbe = 1; } else if (sn->sn_rbe) { log (LOG_INFO, "%s%d: and no rx data to free\n", sn->sn_if.if_name, sn->sn_if.if_unit); snrestart (sn); return (1); } } if (isr & ISR_RDE) { sn->sn_if.if_ierrors++; sn->sn_sum.ss_rbuff++; log (LOG_INFO, "%s%d: rx descriptors exhausted, isr=0x%x\n", sn->sn_if.if_name, sn->sn_if.if_unit, isr); } else if (isr & ISR_RBAE) { sn->sn_if.if_ierrors++; sn->sn_sum.ss_rbuff++; log (LOG_INFO, "%s%d: rx buffer area exceeded, isr=0x%x\n", sn->sn_if.if_name, sn->sn_if.if_unit, isr); } else if (isr & ISR_RFO) { sn->sn_if.if_ierrors++; sn->sn_sum.ss_roflo++; log (LOG_INFO, "%s%d: rx fifo overflow, isr=0x%x\n", sn->sn_if.if_name, sn->sn_if.if_unit, isr); } if (isr & ISR_HBL) sn->sn_sum.ss_noheart++; if (isr & ISR_CRC) sn->sn_crct += 0x10000; if (isr & ISR_FAE) sn->sn_faet += 0x10000; if (isr & ISR_MP) sn->sn_mpt += 0x10000; return (0);}/* * Transmit interrupt routine */static voidsntxint (struct sn_softc *sn){ struct mtd *mtd; register volatile struct TXpkt *txp; register u_int status, ncol; while (mtd = mtdhead) { txp = mtd->mtd_txp; if ((status = MASK(txp->status)) == 0) { /* this packet isn't sent yet */ break; } txp->status = 0;#ifdef DEBUG if (T_txp) { struct ether_header *eh=mtod(mtd->mtd_mbuf,struct ether_header *); printf ("xmit status=0x%x len=%d type=0x%x from %s", status, txp->pkt_size, ntohs(eh->ether_type), ether_sprintf(eh->ether_shost)); printf(" (to %s)\n", ether_sprintf (eh->ether_dhost)); }#endif if (status & TSR_PTX) { /* tx packet has flown, free the packet mbufs */ sntxdone (sn, mtd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -