📄 if_sonic.c
字号:
struct ether_header *eh; caddr_t addr; struct mbuf *m; int len, i; int toff, tlen; /* * Get input data length. * Get pointer to ethernet header (in input buffer). * Deal with trailer protocol: if type is PUP trailer * get true type from first 16-bit word past data. * Remember that type was trailer by setting off. */ len = rxp->byte_count - FCSSIZE; if (IS_K0SEG (rba)) addr = (caddr_t) PHYS_TO_K0 ((rxp->pkt_ptrhi << 16) | rxp->pkt_ptrlo); else addr = (caddr_t) PHYS_TO_K1 ((rxp->pkt_ptrhi << 16) | rxp->pkt_ptrlo);#ifdef MIPSEL snswapb (addr, addr, len);#endif#ifdef XDSSONICBUG xds_compress_buffer (addr, len);#endif eh = (struct ether_header *)addr; eh->ether_type = ntohs((u_short)eh->ether_type); addr += sizeof (struct ether_header); len -= sizeof (struct ether_header); if (len < ETHERMIN || len > ETHERMTU) { log (LOG_WARNING, "%s%d: bad packet length received: %d bytes\n", sn->sn_if.if_name, sn->sn_if.if_unit, len); return 0; } if (eh->ether_type >= ETHERTYPE_TRAIL && eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { toff = (eh->ether_type - ETHERTYPE_TRAIL) * 512; if (toff >= ETHERMTU) { log (LOG_WARNING, "%s%d: trailer offset %d >= ETHERMTU\n", sn->sn_if.if_name, sn->sn_if.if_unit, toff); return 0; } eh->ether_type = ntohs(*(u_short *)(addr + toff)); tlen = ntohs(*(u_short *)(addr + toff + sizeof(u_short))); if (toff + tlen > len) { log (LOG_WARNING, "%s%d: bad trailer toff=%d tlen=%d plen=%d\n", sn->sn_if.if_name, sn->sn_if.if_unit, toff, tlen, len); return 0; } len = toff; /* sizeof data only */ toff += 2*sizeof(u_short); tlen -= 2*sizeof(u_short); } else { toff = tlen = 0; } if (T_rxp) { printf("rcvd 0x%x status=0x%x len=%d type=0x%x from %s", addr, rxp->status, len, eh->ether_type, ether_sprintf (eh->ether_shost)); printf(" (to %s)\n", ether_sprintf (eh->ether_dhost)); } /* * Pull packet off interface. Off is nonzero if packet * has trailing header; sonic_get will then force this header * information to be at the front, but we still have to drop * the type and length which are at the front of any trailer data. */ m = snget (sn, addr, len, toff, tlen); if (!m) return 0; ether_input (&sn->sn_if, eh, m); return 1;}/* * munge the received packet into an mbuf chain * because we are using stupid buffer management this * is slow. */static struct mbuf *snget (struct sn_softc *sn, caddr_t addr, int dlen, int toff, int tlen){ struct mbuf *top = 0; struct mbuf **mp = ⊤ register struct mbuf *m; register int len; caddr_t sp; if (dlen + tlen == 0) return 0; MGETHDR (m, M_DONTWAIT, MT_DATA); if (!m) return 0; m->m_pkthdr.rcvif = &sn->sn_if; m->m_pkthdr.len = dlen + tlen; m->m_len = MHLEN; sp = addr + toff; len = tlen; while (1) { if (len == 0) { if (dlen == 0) return top; /* all done */ sp = addr; len = dlen; dlen = 0; } if (top) { /* get next mbuf */ MGET (m, M_DONTWAIT, MT_DATA); if (!m) { m_freem (top); return 0; } m->m_len = MLEN; } if (len >= MINCLSIZE) { MCLGET (m, M_DONTWAIT); if (m->m_flags & M_EXT) m->m_len = MCLBYTES; } else if (top == 0 && len + max_linkhdr <= m->m_len) { /* place initial small packet/header at end of mbuf */ m->m_data += max_linkhdr; } if (m->m_len > len) m->m_len = len; bcopy (sp, mtod(m, caddr_t), m->m_len); sp += m->m_len; len -= m->m_len; *mp = m; mp = &m->m_next; }}static voidmtd_free (struct mtd *mtd){ mtd->mtd_mbuf = (struct mbuf *)0; mtd->mtd_link = mtdfree; mtdfree = mtd;}static struct mtd *mtd_alloc (){ struct mtd *mtd; mtd = mtdfree; if (mtd) { mtdfree = mtd->mtd_link; mtd->mtd_link = 0; } return (mtd);}/* * CAM support */static voidcaminitialise (){ int i; bzero ((char *)cda, CDASIZE); for (i = 0; i < MAXCAM; i++) cda->desc[i].cam_ep = i; cda->enable = 0;}static voidcamentry (int entry, u_char *ea){ cda->desc[entry].cam_ep = entry; cda->desc[entry].cam_ap2 = (ea[5]<<8) | ea[4]; cda->desc[entry].cam_ap1 = (ea[3]<<8) | ea[2]; cda->desc[entry].cam_ap0 = (ea[1]<<8) | ea[0]; cda->enable |= (1 << entry);}static intcamprogram(struct sn_softc *sn){ volatile struct sn_reg *csr; int timeout; int i; csr = sn->sn_csr; csr->s_cdp = LOWER(cda); csr->s_cdc = MAXCAM; wbflush (); csr->s_cr = CR_LCAM; wbflush (); timeout = 1000; while ((csr->s_cr & CR_LCAM) && --timeout > 0) DELAY (100); /* let it get at the bus */ if (timeout <= 0) { log (LOG_ERR, "sonic: CAM init failed\n"); return 0; } timeout = 1000; while ((csr->s_isr & ISR_LCD) == 0 && --timeout > 0) DELAY (100); csr->s_isr = ISR_LCD; wbflush(); if (timeout <= 0) { log (LOG_ERR, "sonic: CAM init didn't interrupt\n"); return 0; } return 1;}#ifdef notdefstatic voidcamdump(){ printf ("CAM entries:\n"); csr->s_cr = CR_RST; wbflush (); for (i = 0; i < 16; i++) { u_short ap2, ap1, ap0; csr->s_cep = i; wbflush (); ap2 = csr->s_cap2; ap1 = csr->s_cap1; ap0 = csr->s_cap0; printf ("%d: ap2=0x%x ap1=0x%x ap0=0x%x\n", i, ap2, ap1, ap0); } printf ("CAM enable 0x%x\n", csr->s_cep); csr->s_cr = 0; wbflush ();}#endif/* * because the sonic is basically a 16 bit device it 'concatenates' * a higher buffer address to a 16 bit offset this can cause wrap * around problems near 64k boundaries !! */static intallocatebuffers (){ void *buf; u_long p, lo, hi;#if defined(XDS) rba = (char *)PHYS_TO_K0(SRAM_NET); tba = rba + NRBA * RBASIZE; buf = tba + NTDA * TBASIZE;#else buf = malloc (SONICBUFSIZE, M_DEVBUF, 0); if (!buf) return (ENOBUFS); rba = (char *) malloc (NRBA * RBASIZE, M_DEVBUF, 0); if (!rba) { free (buf, M_DEVBUF); return (ENOBUFS); }#endif#ifdef R4000 /* flush dirty data first */ if (IS_K0SEG (buf)) { clean_dcache (buf, SONICBUFSIZE); clean_dcache (rba, NRBA * RBASIZE);#ifdef XDS clean_dcache (tba, NTDA * TBASIZE);#endif }#endif /* force into kseg1, and align correctly */ p = ((u_long) K0_TO_K1 (buf) + SONICALIGN - 1) & ~(SONICALIGN - 1); /* RRA and CDA must fit inside 64k region */ if ((p ^ (p+RRASIZE+CDASIZE)) & ~0xffff) p = (p+0x10000) & ~0xffff; rra = (struct RXrsrc *)p; p += RRASIZE; cda = (struct CDA *)p; p += CDASIZE; /* RDA must fit inside 64k region */ if ((p ^ (p+RDASIZE)) & ~0xffff) p = (p+0x10000) & ~0xffff; rda = (struct RXpkt *)p; p += RDASIZE; /* TDA must fit inside 64k region */ if ((p ^ (p+TDASIZE)) & ~0xffff) p = (p+0x10000) & ~0xffff; tda = (struct TXpkt *)p; p += TDASIZE; /* check sanity of buffer addresese */ lo = (u_long) K0_TO_K1 (buf); hi = lo + SONICBUFSIZE; if ((unsigned)rra < lo || (unsigned)rra >= hi || (unsigned)cda < lo || (unsigned)cda >= hi || (unsigned)rda < lo || (unsigned)rda >= hi || (unsigned)tda < lo || (unsigned)tda >= hi) { log (LOG_ERR, "sonic descriptors out of range\n"); return ENOMEM; } /* return errno */ return 0;}static voidinitialise_tda (struct sn_softc *sn){ volatile struct sn_reg *csr = sn->sn_csr; struct mtd *mtd; int i; bzero ((char *)tda, TDASIZE); mtdfree = mtdhead = mtdtail = (struct mtd *)0; for (i = 0; i < NTDA; i++) { mtd = &mtda[i]; mtd->mtd_txp = &tda[i]; mtd_free(mtd);#ifdef XDS mtd->mtd_tba = tba + i*TBASIZE;#endif } mtdnext = mtd_alloc (); csr->s_utda = UPPER(tda); wbflush(); csr->s_ctda = LOWER(mtdnext->mtd_txp); wbflush();}static voidinitialise_rda (struct sn_softc *sn){ volatile struct sn_reg *csr = sn->sn_csr; int i; /* link the RDA's together into a circular list */ bzero ((char *)rda, RDASIZE); for (i = 0; i < (NRDA-1); i++) { rda[i].rlink = LOWER(&rda[i+1]); rda[i].in_use = ~0; } rda[NRDA-1].in_use = ~0; rda[NRDA-1].rlink = LOWER(&rda[0]) | EOL; /* mark end of receive descriptor list */ sn->sn_lrxp = &rda[NRDA-1]; sn->sn_rxmark = 0; csr->s_urda = UPPER(rda); csr->s_crda = LOWER(rda); wbflush();}static voidinitialise_rra (struct sn_softc *sn){ volatile struct sn_reg *csr = sn->sn_csr; char *rb; int i; bzero ((char *)rra, RRASIZE); csr->s_eobc = EOBC / 2; csr->s_urra = UPPER(rra); csr->s_rsa = LOWER(rra); csr->s_rea = LOWER(&rra[NRRA]); /* * fill up SOME of the rra with buffers */ for (i = 0, rb = rba; i < NRBA; i++, rb += RBASIZE) { rra[i].buff_ptrhi = UPPER(rb); rra[i].buff_ptrlo = LOWER(rb);#ifdef XDSSONICBUG rra[i].buff_wchi = (RBASIZE / 4) >> 16; rra[i].buff_wclo = (RBASIZE / 4);#else rra[i].buff_wchi = (RBASIZE / 2) >> 16; rra[i].buff_wclo = (RBASIZE / 2);#endif } sn->sn_rramark = NRBA; csr->s_rrp = LOWER(rra); csr->s_rwp = LOWER(&rra[sn->sn_rramark]); wbflush();}/* * snwatch(): interface watchdog timer * * Called if any Tx packets remain unsent after 5 seconds, * In all cases we just reset the chip, and any retransmission * will be handled by higher level protocol timeouts. */static intsnwatch (int unit){ struct sn_softc *sn = &sn_softc[unit]; int s = splimp(); if (mtdhead && mtdhead != mtdnext) { /* something still pending for transmit */ log (LOG_WARNING, "%s%d: Tx - %s\n", sn->sn_if.if_name, sn->sn_if.if_unit, (mtdhead->mtd_txp->status == 0) ? "timeout" : "lost interrupt"); snrestart (sn); } (void) splx (s); return 0;}/* * sn_pullup(): reorganise part of an mbuf chain to satisfy SONIC. * * It doesn't free mbufs that reduce to zero length. * This is in case they are NFS mbufs which point to disk buffers. * and the buffers must not be released until after the packet has flown. */static struct mbuf *sn_pullup (n, len) register struct mbuf *n; register int len;{ register struct mbuf *m; register int count; /* check it will fit in an ordinary mbuf */ if (len > MLEN) goto bad; if ((n->m_flags & M_EXT) == 0) { /* ordinary mbuf */ if (n->m_data + len > &n->m_dat[MLEN]) { /* not enough space: must realign buffer start */ bcopy (n->m_data, n->m_dat, n->m_len); n->m_data = n->m_dat; } } else { /* external cluster (must be only user) */ if (mclrefcnt[mtocl(n->m_data)] != 1) { log (LOG_NOTICE, "snpullup: shared ext cluster\n"); goto bad; } if (n->m_data + len > n->m_ext.ext_buf + n->m_ext.ext_size) { /* not enough space: must realign cluster start */#ifdef DEBUG if (len <= n->m_ext.ext_size) { log (LOG_NOTICE, "snpullup: ext buf too small (%d < %d)\n", n->m_ext.ext_size, len); goto bad; }#endif bcopy (n->m_data, n->m_ext.ext_buf, n->m_len); n->m_data = n->m_ext.ext_buf; } } /* now ok to append in place */ m = n; n = n->m_next; len -= m->m_len; while (len > 0 && n) { count = MIN(len, n->m_len); bcopy(mtod(n, caddr_t), mtod(m, caddr_t)+m->m_len, (unsigned)count); len -= count; m->m_len += count; n->m_len -= count; if (n->m_len) n->m_data += count; else n = n->m_next; } return (m); bad: m_freem(n); return (0);}/* * Fillup a whole chain, so that each mbuf has a sensible minimum * length, to avoid transmit underflows. */static struct mbuf *sn_fillup (m, minlen) register struct mbuf *m; register int minlen;{ struct mbuf *m0 = 0; register struct mbuf *mprev = (struct mbuf *)&m0; while (m) { if (m->m_len && m->m_len < minlen && m->m_next) m = sn_pullup (m, minlen); mprev->m_next = m; mprev = m; m = m->m_next; } return m0;}#ifdef MIPSELstatic voidsnswapb (s, d, len) u_char *s, *d; int len;{ while (len > 0) { *(unsigned int *)d = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; d += 4; s += 4; len -= 4; }}static struct mbuf *snswapm (m0) struct mbuf *m0;{ struct mbuf *m; caddr_t s; for (m = m0; m; m = m->m_next) { if ((m->m_flags & M_EXT) && mclrefcnt[mtocl(m->m_data)] != 1) /* external cluster (must be only user) */ panic ("snswapm: shared ext cluster"); s = m->m_data; if ((unsigned)s & 3) { /* buffer is unaligned: must align data in buffer */ caddr_t dat, d; unsigned int msz; /* determine data buffer base and size */ if (m->m_flags & M_EXT) { dat = m->m_ext.ext_buf; msz = m->m_ext.ext_size; } else if (m->m_flags & M_PKTHDR) { dat = m->m_pktdat; msz = MHLEN; } else { dat = m->m_dat; msz = MLEN; } /* round buffer base up to word boundary */ d = (caddr_t) (((unsigned long)dat + 3) & ~3); /* can we shuffle the data down to start of buffer? */ if (m->m_data > d) m->m_data = d; else { /* is there room after the current data? */ d = (caddr_t) (((unsigned long)m->m_data + m->m_len + 3) & ~3); if (d + ((m->m_len + 3) & ~3) <= dat + msz) m->m_data = d; else /* this shouldn't happen!! */ panic ("snswapm"); } } snswapb (s, m->m_data, m->m_len); } return m0;}#endif#ifdef XDSSONICBUG/* * compress buffer in SRAM after it has been received */voidxds_compress_buffer (addr, len)caddr_t addr;int len;{ int words = (len + 3) >> 2; unsigned int *sp, *dp; /* ASSERT(IS_K0SEG(addr)); */ sp = dp = (unsigned int *)addr; while (words-- > 0) { *sp = *dp; sp++; dp += 2; }}/* * expand buffer in SRAM before it is sent */voidxds_expand_buffer (addr, len)caddr_t addr;int len;{ int words = (len + 3) >> 2; unsigned int *sp, *dp; /* ASSERT(IS_K0SEG(addr)); */ sp = (unsigned int *)addr + words; dp = sp + words; while (words-- > 0) { *(dp+1) = 0xdeaddead; *dp = *sp; sp--; dp -= 2; } clean_dcache (addr, len * 2);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -