midway.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,376 行 · 第 1/5 页
C
2,376 行
/* * en_b2sz: convert a DMA burst code to its byte size */STATIC INLINE int en_b2sz(b)int b;{ switch (b) { case MIDDMA_WORD: return(1*4); case MIDDMA_2WMAYBE: case MIDDMA_2WORD: return(2*4); case MIDDMA_4WMAYBE: case MIDDMA_4WORD: return(4*4); case MIDDMA_8WMAYBE: case MIDDMA_8WORD: return(8*4); case MIDDMA_16WMAYBE: case MIDDMA_16WORD: return(16*4); default: panic("en_b2sz"); } return(0);}/* * en_sz2b: convert a burst size (bytes) to DMA burst code */STATIC INLINE int en_sz2b(sz)int sz;{ switch (sz) { case 1*4: return(MIDDMA_WORD); case 2*4: return(MIDDMA_2WORD); case 4*4: return(MIDDMA_4WORD); case 8*4: return(MIDDMA_8WORD); case 16*4: return(MIDDMA_16WORD); default: panic("en_sz2b"); } return(0);}/* * en_dqneed: calculate number of DTQ/DRQ's needed for a buffer */STATIC INLINE int en_dqneed(sc, data, len, tx)struct en_softc *sc;caddr_t data;u_int len, tx;{ int result, needalign, sz;#if !defined(MIDWAY_ENIONLY)#if !defined(MIDWAY_ADPONLY) if (sc->is_adaptec)#endif /* !MIDWAY_ADPONLY */ return(1); /* adaptec can DMA anything in one go */#endif #if !defined(MIDWAY_ADPONLY) result = 0; if (len < EN_MINDMA) { if (!tx) /* XXX: conservative */ return(1); /* will copy/DMA_JK */ } if (tx) { /* byte burst? */ needalign = (((uintptr_t) (void *) data) % sizeof(u_int32_t)); if (needalign) { result++; sz = min(len, sizeof(u_int32_t) - needalign); len -= sz; data += sz; } } if (sc->alburst && len) { needalign = (((uintptr_t) (void *) data) & sc->bestburstmask); if (needalign) { result++; /* alburst */ sz = min(len, sc->bestburstlen - needalign); len -= sz; } } if (len >= sc->bestburstlen) { sz = len / sc->bestburstlen; sz = sz * sc->bestburstlen; len -= sz; result++; /* best shot */ } if (len) { result++; /* clean up */ if (tx && (len % sizeof(u_int32_t)) != 0) result++; /* byte cleanup */ } return(result);#endif /* !MIDWAY_ADPONLY */}/* * en_mget: get an mbuf chain that can hold totlen bytes and return it * (for recv) [based on am7990_get from if_le and ieget from if_ie] * after this call the sum of all the m_len's in the chain will be totlen. */STATIC INLINE struct mbuf *en_mget(sc, totlen, drqneed)struct en_softc *sc;u_int totlen, *drqneed;{ struct mbuf *m; struct mbuf *top, **mp; *drqneed = 0; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return(NULL); m->m_pkthdr.rcvif = &sc->enif; m->m_pkthdr.len = totlen; m->m_len = MHLEN; top = NULL; mp = ⊤ /* if (top != NULL) then we've already got 1 mbuf on the chain */ while (totlen > 0) { if (top) { MGET(m, M_DONTWAIT, MT_DATA); if (!m) { m_freem(top); return(NULL); /* out of mbufs */ } m->m_len = MLEN; } if (totlen >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); m_freem(top); return(NULL); /* out of mbuf clusters */ } m->m_len = MCLBYTES; } m->m_len = min(totlen, m->m_len); totlen -= m->m_len; *mp = m; mp = &m->m_next; *drqneed += en_dqneed(sc, m->m_data, m->m_len, 0); } return(top);}/***********************************************************************//* * autoconfig stuff */void en_attach(sc)struct en_softc *sc;{ struct ifnet *ifp = &sc->enif; int sz; u_int32_t reg, lcv, check, ptr, sav, midvloc; /* * probe card to determine memory size. the stupid ENI card always * reports to PCI that it needs 4MB of space (2MB regs and 2MB RAM). * if it has less than 2MB RAM the addresses wrap in the RAM address space. * (i.e. on a 512KB card addresses 0x3ffffc, 0x37fffc, and 0x2ffffc * are aliases for 0x27fffc [note that RAM starts at offset 0x200000]). */ if (sc->en_busreset) sc->en_busreset(sc); EN_WRITE(sc, MID_RESID, 0x0); /* reset card before touching RAM */ for (lcv = MID_PROBEOFF; lcv <= MID_MAXOFF ; lcv += MID_PROBSIZE) { EN_WRITE(sc, lcv, lcv); /* data[address] = address */ for (check = MID_PROBEOFF ; check < lcv ; check += MID_PROBSIZE) { reg = EN_READ(sc, check); if (reg != check) { /* found an alias! */ goto done_probe; /* and quit */ } } }done_probe: lcv -= MID_PROBSIZE; /* take one step back */ sc->en_obmemsz = (lcv + 4) - MID_RAMOFF; /* * determine the largest DMA burst supported */ en_dmaprobe(sc); /* * "hello world" */ if (sc->en_busreset) sc->en_busreset(sc); EN_WRITE(sc, MID_RESID, 0x0); /* reset */ for (lcv = MID_RAMOFF ; lcv < MID_RAMOFF + sc->en_obmemsz ; lcv += 4) EN_WRITE(sc, lcv, 0); /* zero memory */ reg = EN_READ(sc, MID_RESID); printf("%s: ATM midway v%d, board IDs %d.%d, %s%s%s, %ldKB on-board RAM\n", sc->sc_dev.dv_xname, MID_VER(reg), MID_MID(reg), MID_DID(reg), (MID_IS_SABRE(reg)) ? "sabre controller, " : "", (MID_IS_SUNI(reg)) ? "SUNI" : "Utopia", (!MID_IS_SUNI(reg) && MID_IS_UPIPE(reg)) ? " (pipelined)" : "", (long)(sc->en_obmemsz / 1024)); if (sc->is_adaptec) { if (sc->bestburstlen == 64 && sc->alburst == 0) printf("%s: passed 64 byte DMA test\n", sc->sc_dev.dv_xname); else printf("%s: FAILED DMA TEST: burst=%d, alburst=%d\n", sc->sc_dev.dv_xname, sc->bestburstlen, sc->alburst); } else { printf("%s: maximum DMA burst length = %d bytes%s\n", sc->sc_dev.dv_xname, sc->bestburstlen, (sc->alburst) ? " (must align)" : ""); } /* * link into network subsystem and prepare card */#if defined(__NetBSD__) || defined(__OpenBSD__) bcopy(sc->sc_dev.dv_xname, sc->enif.if_xname, IFNAMSIZ);#endif sc->enif.if_softc = sc; ifp->if_flags = IFF_SIMPLEX|IFF_NOTRAILERS; ifp->if_ioctl = en_ioctl; ifp->if_output = atm_output; ifp->if_start = en_start; /* * init softc */ for (lcv = 0 ; lcv < MID_N_VC ; lcv++) { sc->rxvc2slot[lcv] = RX_NONE; sc->txspeed[lcv] = 0; /* full */ sc->txvc2slot[lcv] = 0; /* full speed == slot 0 */ } sz = sc->en_obmemsz - (MID_BUFOFF - MID_RAMOFF); ptr = sav = MID_BUFOFF; ptr = roundup(ptr, EN_TXSZ * 1024); /* align */ sz = sz - (ptr - sav); if (EN_TXSZ*1024 * EN_NTX > sz) { printf("%s: EN_NTX/EN_TXSZ too big\n", sc->sc_dev.dv_xname); return; } for (lcv = 0 ; lcv < EN_NTX ; lcv++) { sc->txslot[lcv].mbsize = 0; sc->txslot[lcv].start = ptr; ptr += (EN_TXSZ * 1024); sz -= (EN_TXSZ * 1024); sc->txslot[lcv].stop = ptr; sc->txslot[lcv].nref = 0; bzero(&sc->txslot[lcv].indma, sizeof(sc->txslot[lcv].indma)); bzero(&sc->txslot[lcv].q, sizeof(sc->txslot[lcv].q));#ifdef EN_DEBUG printf("%s: tx%d: start 0x%x, stop 0x%x\n", sc->sc_dev.dv_xname, lcv, sc->txslot[lcv].start, sc->txslot[lcv].stop);#endif } sav = ptr; ptr = roundup(ptr, EN_RXSZ * 1024); /* align */ sz = sz - (ptr - sav); sc->en_nrx = sz / (EN_RXSZ * 1024); if (sc->en_nrx <= 0) { printf("%s: EN_NTX/EN_TXSZ/EN_RXSZ too big\n", sc->sc_dev.dv_xname); return; } /* * ensure that there is always one VC slot on the service list free * so that we can tell the difference between a full and empty list. */ if (sc->en_nrx >= MID_N_VC) sc->en_nrx = MID_N_VC - 1; for (lcv = 0 ; lcv < sc->en_nrx ; lcv++) { sc->rxslot[lcv].rxhand = NULL; sc->rxslot[lcv].oth_flags = ENOTHER_FREE; bzero(&sc->rxslot[lcv].indma, sizeof(sc->rxslot[lcv].indma)); bzero(&sc->rxslot[lcv].q, sizeof(sc->rxslot[lcv].q)); midvloc = sc->rxslot[lcv].start = ptr; ptr += (EN_RXSZ * 1024); sz -= (EN_RXSZ * 1024); sc->rxslot[lcv].stop = ptr; midvloc = midvloc - MID_RAMOFF; midvloc = (midvloc & ~((EN_RXSZ*1024) - 1)) >> 2; /* mask, cvt to words */ midvloc = midvloc >> MIDV_LOCTOPSHFT; /* we only want the top 11 bits */ midvloc = (midvloc & MIDV_LOCMASK) << MIDV_LOCSHIFT; sc->rxslot[lcv].mode = midvloc | (en_k2sz(EN_RXSZ) << MIDV_SZSHIFT) | MIDV_TRASH;#ifdef EN_DEBUG printf("%s: rx%d: start 0x%x, stop 0x%x, mode 0x%x\n", sc->sc_dev.dv_xname, lcv, sc->rxslot[lcv].start, sc->rxslot[lcv].stop, sc->rxslot[lcv].mode);#endif }#ifdef EN_STAT sc->vtrash = sc->otrash = sc->mfix = sc->txmbovr = sc->dmaovr = 0; sc->txoutspace = sc->txdtqout = sc->launch = sc->lheader = sc->ltail = 0; sc->hwpull = sc->swadd = sc->rxqnotus = sc->rxqus = sc->rxoutboth = 0; sc->rxdrqout = sc->ttrash = sc->rxmbufout = sc->mfixfail = 0; sc->headbyte = sc->tailbyte = sc->tailflush = 0;#endif sc->need_drqs = sc->need_dtqs = 0; printf("%s: %d %dKB receive buffers, %d %dKB transmit buffers allocated\n", sc->sc_dev.dv_xname, sc->en_nrx, EN_RXSZ, EN_NTX, EN_TXSZ); printf("%s: End Station Identifier (mac address) %6D\n", sc->sc_dev.dv_xname, sc->macaddr, ":"); /* * final commit */ if_attach(ifp); atm_ifattach(ifp); #if NBPFILTER > 0 BPFATTACH(ifp, DLT_ATM_RFC1483, sizeof(struct atmllc));#endif}/* * en_dmaprobe: helper function for en_attach. * * see how the card handles DMA by running a few DMA tests. we need * to figure out the largest number of bytes we can DMA in one burst * ("bestburstlen"), and if the starting address for a burst needs to * be aligned on any sort of boundary or not ("alburst"). * * typical findings: * sparc1: bestburstlen=4, alburst=0 (ick, broken DMA!) * sparc2: bestburstlen=64, alburst=1 * p166: bestburstlen=64, alburst=0 */#if 1 /* __FreeBSD__ */#define NBURSTS 3 /* number of bursts to use for dmaprobe */#define BOUNDARY 1024 /* test misaligned dma crossing the bounday. should be n * 64. at least 64*(NBURSTS+1). dell P6 with EDO DRAM has 1K bounday problem */#endifSTATIC void en_dmaprobe(sc)struct en_softc *sc;{#ifdef NBURSTS /* be careful. kernel stack is only 8K */ u_int8_t buffer[BOUNDARY * 2 + 64 * (NBURSTS + 1)]; #else u_int32_t srcbuf[64], dstbuf[64];#endif u_int8_t *sp, *dp; int bestalgn, bestnotalgn, lcv, try; sc->alburst = 0;#ifdef NBURSTS /* setup src and dst buf at the end of the boundary */ sp = (u_int8_t *)roundup((uintptr_t)(void *)buffer, 64); while (((uintptr_t)(void *)sp & (BOUNDARY - 1)) != (BOUNDARY - 64)) sp += 64; dp = sp + BOUNDARY; /* * we can't dma across page boundary so that, if buf is at a page * boundary, move it to the next page. but still either src or dst * will be at the boundary, which should be ok. */ if ((((uintptr_t)(void *)sp + 64) & PAGE_MASK) == 0) sp += 64; if ((((uintptr_t)(void *)dp + 64) & PAGE_MASK) == 0) dp += 64;#else /* !NBURSTS */ sp = (u_int8_t *) srcbuf; while ((((unsigned long) sp) % MIDDMA_MAXBURST) != 0) sp += 4; dp = (u_int8_t *) dstbuf; while ((((unsigned long) dp) % MIDDMA_MAXBURST) != 0) dp += 4;#endif /* !NBURSTS */ bestalgn = bestnotalgn = en_dmaprobe_doit(sc, sp, dp, 0); for (lcv = 4 ; lcv < MIDDMA_MAXBURST ; lcv += 4) { try = en_dmaprobe_doit(sc, sp+lcv, dp+lcv, 0);#ifdef NBURSTS if (try < bestnotalgn) { bestnotalgn = try; break; }#else if (try < bestnotalgn) bestnotalgn = try;#endif } if (bestalgn != bestnotalgn) /* need bursts aligned */ sc->alburst = 1; sc->bestburstlen = bestalgn; sc->bestburstshift = en_log2(bestalgn); sc->bestburstmask = sc->bestburstlen - 1; /* must be power of 2 */ sc->bestburstcode = en_sz2b(bestalgn);#if 1 /* __FreeBSD__ */ /* * correct pci chipsets should be able to handle misaligned-64-byte DMA. * but there are too many broken chipsets around. we try to work around * by finding the best workable dma size, but still some broken machines * exhibit the problem later. so warn it here. */ if (bestalgn != 64 || sc->alburst != 0) { printf("%s: WARNING: DMA test detects a broken PCI chipset!\n", sc->sc_dev.dv_xname); printf(" trying to work around the problem... but if this doesn't\n"); printf(" work for you, you'd better switch to a newer motherboard.\n"); }#endif /* 1 */ return;}/* * en_dmaprobe_doit: do actual testing */inten_dmaprobe_doit(sc, sp, dp, wmtry)struct en_softc *sc;u_int8_t *sp, *dp;int wmtry;{ int lcv, retval = 4, cnt, count; u_int32_t reg, bcode, midvloc; /* * set up a 1k buffer at MID_BUFOFF */ if (sc->en_busreset) sc->en_busreset(sc);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?