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 = &top;    /* 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 + -
显示快捷键?