if_pn.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,280 行 · 第 1/4 页
C
2,280 行
ifp->if_ioctl = pn_ioctl; ifp->if_output = ether_output; ifp->if_start = pn_start; ifp->if_watchdog = pn_watchdog; ifp->if_init = pn_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = PN_TX_LIST_CNT - 1; ifmedia_init(&sc->ifmedia, 0, pn_ifmedia_upd, pn_ifmedia_sts); if (bootverbose) printf("pn%d: probing for a PHY\n", sc->pn_unit); for (i = PN_PHYADDR_MIN; i < PN_PHYADDR_MAX + 1; i++) { if (bootverbose) printf("pn%d: checking address: %d\n", sc->pn_unit, i); sc->pn_phy_addr = i; pn_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); DELAY(500); while(pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); if ((phy_sts = pn_phy_readreg(sc, PHY_BMSR))) break; } if (phy_sts) { phy_vid = pn_phy_readreg(sc, PHY_VENID); phy_did = pn_phy_readreg(sc, PHY_DEVID); if (bootverbose) printf("pn%d: found PHY at address %d, ", sc->pn_unit, sc->pn_phy_addr); if (bootverbose) printf("vendor id: %x device id: %x\n", phy_vid, phy_did); p = pn_phys; while(p->pn_vid) { if (phy_vid == p->pn_vid && (phy_did | 0x000F) == p->pn_did) { sc->pn_pinfo = p; break; } p++; } if (sc->pn_pinfo == NULL) sc->pn_pinfo = &pn_phys[PHY_UNKNOWN]; if (bootverbose) printf("pn%d: PHY type: %s\n", sc->pn_unit, sc->pn_pinfo->pn_name); pn_getmode_mii(sc); pn_autoneg_mii(sc, PN_FLAG_FORCEDELAY, 1); } else { ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); pn_autoneg(sc, PN_FLAG_FORCEDELAY, 1); } media = sc->ifmedia.ifm_media; pn_stop(sc); ifmedia_set(&sc->ifmedia, media); /* * Call MI attach routines. */ if_attach(ifp); ether_ifattach(ifp);#if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));#endif at_shutdown(pn_shutdown, sc, SHUTDOWN_POST_SYNC);fail: splx(s); return;}/* * Initialize the transmit descriptors. */static int pn_list_tx_init(sc) struct pn_softc *sc;{ struct pn_chain_data *cd; struct pn_list_data *ld; int i; cd = &sc->pn_cdata; ld = sc->pn_ldata; for (i = 0; i < PN_TX_LIST_CNT; i++) { cd->pn_tx_chain[i].pn_ptr = &ld->pn_tx_list[i]; if (i == (PN_TX_LIST_CNT - 1)) cd->pn_tx_chain[i].pn_nextdesc = &cd->pn_tx_chain[0]; else cd->pn_tx_chain[i].pn_nextdesc = &cd->pn_tx_chain[i + 1]; } cd->pn_tx_free = &cd->pn_tx_chain[0]; cd->pn_tx_tail = cd->pn_tx_head = NULL; return(0);}/* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */static int pn_list_rx_init(sc) struct pn_softc *sc;{ struct pn_chain_data *cd; struct pn_list_data *ld; int i; cd = &sc->pn_cdata; ld = sc->pn_ldata; for (i = 0; i < PN_RX_LIST_CNT; i++) { cd->pn_rx_chain[i].pn_ptr = (struct pn_desc *)&ld->pn_rx_list[i]; if (pn_newbuf(sc, &cd->pn_rx_chain[i]) == ENOBUFS) return(ENOBUFS); if (i == (PN_RX_LIST_CNT - 1)) { cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[0]; ld->pn_rx_list[i].pn_next = vtophys(&ld->pn_rx_list[0]); } else { cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[i + 1]; ld->pn_rx_list[i].pn_next = vtophys(&ld->pn_rx_list[i + 1]); } } cd->pn_rx_head = &cd->pn_rx_chain[0]; return(0);}/* * Initialize an RX descriptor and attach an MBUF cluster. * Note: the length fields are only 11 bits wide, which means the * largest size we can specify is 2047. This is important because * MCLBYTES is 2048, so we have to subtract one otherwise we'll * overflow the field and make a mess. */static int pn_newbuf(sc, c) struct pn_softc *sc; struct pn_chain_onefrag *c;{ struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) { printf("pn%d: no memory for rx list -- packet dropped!\n", sc->pn_unit); return(ENOBUFS); } MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { printf("pn%d: no memory for rx list -- packet dropped!\n", sc->pn_unit); m_freem(m_new); return(ENOBUFS); } /* * Zero the buffer. This is part of the workaround for the * promiscuous mode bug in the revision 33 PNIC chips. */ bzero((char *)mtod(m_new, char *), MCLBYTES); m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; c->pn_mbuf = m_new; c->pn_ptr->pn_status = PN_RXSTAT; c->pn_ptr->pn_data = vtophys(mtod(m_new, caddr_t)); c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; return(0);}#ifdef PN_RX_BUG_WAR/* * Grrrrr. * The PNIC chip has a terrible bug in it that manifests itself during * periods of heavy activity. The exact mode of failure if difficult to * pinpoint: sometimes it only happens in promiscuous mode, sometimes it * will happen on slow machines. The bug is that sometimes instead of * uploading one complete frame during reception, it uploads what looks * like the entire contents of its FIFO memory. The frame we want is at * the end of the whole mess, but we never know exactly how much data has * been uploaded, so salvaging the frame is hard. * * There is only one way to do it reliably, and it's disgusting. * Here's what we know: * * - We know there will always be somewhere between one and three extra * descriptors uploaded. * * - We know the desired received frame will always be at the end of the * total data upload. * * - We know the size of the desired received frame because it will be * provided in the length field of the status word in the last descriptor. * * Here's what we do: * * - When we allocate buffers for the receive ring, we bzero() them. * This means that we know that the buffer contents should be all * zeros, except for data uploaded by the chip. * * - We also force the PNIC chip to upload frames that include the * ethernet CRC at the end. * * - We gather all of the bogus frame data into a single buffer. * * - We then position a pointer at the end of this buffer and scan * backwards until we encounter the first non-zero byte of data. * This is the end of the received frame. We know we will encounter * some data at the end of the frame because the CRC will always be * there, so even if the sender transmits a packet of all zeros, * we won't be fooled. * * - We know the size of the actual received frame, so we subtract * that value from the current pointer location. This brings us * to the start of the actual received packet. * * - We copy this into an mbuf and pass it on, along with the actual * frame length. * * The performance hit is tremendous, but it beats dropping frames all * the time. */#define PN_WHOLEFRAME (PN_RXSTAT_FIRSTFRAG|PN_RXSTAT_LASTFRAG)static void pn_rx_bug_war(sc, cur_rx) struct pn_softc *sc; struct pn_chain_onefrag *cur_rx;{ struct pn_chain_onefrag *c; unsigned char *ptr; int total_len; u_int32_t rxstat = 0; c = sc->pn_rx_bug_save; ptr = sc->pn_rx_buf; bzero(ptr, sizeof(PN_RXLEN * 5)); /* Copy all the bytes from the bogus buffers. */ while ((c->pn_ptr->pn_status & PN_WHOLEFRAME) != PN_WHOLEFRAME) { rxstat = c->pn_ptr->pn_status; m_copydata(c->pn_mbuf, 0, PN_RXLEN, ptr); ptr += PN_RXLEN - 2; /* round down to 32-bit boundary */ if (c == cur_rx) break; if (rxstat & PN_RXSTAT_LASTFRAG) break; c->pn_ptr->pn_status = PN_RXSTAT; c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(c->pn_mbuf, char *), MCLBYTES); c = c->pn_nextdesc; } /* Find the length of the actual receive frame. */ total_len = PN_RXBYTES(rxstat); /* Scan backwards until we hit a non-zero byte. */ while(*ptr == 0x00) ptr--; /* Round off. */ if ((u_int32_t)(ptr) & 0x3) ptr -= 1; /* Now find the start of the frame. */ ptr -= total_len; if (ptr < sc->pn_rx_buf) ptr = sc->pn_rx_buf; /* * Now copy the salvaged frame to the last mbuf and fake up * the status word to make it look like a successful * frame reception. */ m_copyback(cur_rx->pn_mbuf, 0, total_len, ptr); cur_rx->pn_mbuf->m_len = c->pn_mbuf->m_pkthdr.len = MCLBYTES; cur_rx->pn_ptr->pn_status |= PN_RXSTAT_FIRSTFRAG; return;}#endif/* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */static void pn_rxeof(sc) struct pn_softc *sc;{ struct ether_header *eh; struct mbuf *m; struct ifnet *ifp; struct pn_chain_onefrag *cur_rx; int total_len = 0; u_int32_t rxstat; ifp = &sc->arpcom.ac_if; while(!((rxstat = sc->pn_cdata.pn_rx_head->pn_ptr->pn_status) & PN_RXSTAT_OWN)) {#ifdef __alpha__ struct mbuf *m0 = NULL;#endif cur_rx = sc->pn_cdata.pn_rx_head; sc->pn_cdata.pn_rx_head = cur_rx->pn_nextdesc;#ifdef PN_RX_BUG_WAR /* * XXX The PNIC has a nasty receiver bug that manifests * under certain conditions (sometimes only in promiscuous * mode, sometimes only on slow machines even when not in * promiscuous mode). We have to keep an eye out for the * failure condition and employ a workaround to recover * any mangled frames. */ if (sc->pn_rx_war) { if ((rxstat & PN_WHOLEFRAME) != PN_WHOLEFRAME) { if (rxstat & PN_RXSTAT_FIRSTFRAG) sc->pn_rx_bug_save = cur_rx; if ((rxstat & PN_RXSTAT_LASTFRAG) == 0) continue; pn_rx_bug_war(sc, cur_rx); rxstat = cur_rx->pn_ptr->pn_status; } }#endif /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & PN_RXSTAT_RXERR) { ifp->if_ierrors++; if (rxstat & PN_RXSTAT_COLLSEEN) ifp->if_collisions++; cur_rx->pn_ptr->pn_status = PN_RXSTAT; cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES); continue; } /* No errors; receive the packet. */ m = cur_rx->pn_mbuf; total_len = PN_RXBYTES(cur_rx->pn_ptr->pn_status); /* Trim off the CRC. */ total_len -= ETHER_CRC_LEN; /* * Try to conjure up a new mbuf cluster. If that * fails, it means we have an out of memory condition and * should leave the buffer in place and continue. This will * result in a lost packet, but there's little else we * can do in this situation. */ if (pn_newbuf(sc, cur_rx) == ENOBUFS) { ifp->if_ierrors++; cur_rx->pn_ptr->pn_status = PN_RXSTAT; cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES); continue; }#ifdef __alpha__ /* * Grrrr! On the alpha platform, the start of the * packet data must be longword aligned so that ip_input() * doesn't perform any unaligned accesses when it tries * to fiddle with the IP header. But the PNIC is stupid * and wants RX buffers to start on longword boundaries. * So we can't just shift the DMA address over a few * bytes to alter the payload alignment. Instead, we * have to chop out ethernet and IP header parts of * the packet and place then in a separate mbuf with * the alignment fixed up properly. * * As if this chip wasn't broken enough already. */ MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 == NULL) { ifp->if_ierrors++; cur_rx->pn_ptr->pn_status = PN_RXSTAT; cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES); continue; } m0->m_data += 2; if (total_len <= (MHLEN - 2)) { bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), total_len); m_freem(m); m = m0; m->m_pkthdr.len = m->m_len = total_len; } else { bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), (MHLEN - 2)); m->m_len = total_len - (MHLEN - 2); m->m_data += (MHLEN - 2); m0->m_next = m; m0->m_len = (MHLEN - 2); m = m0; m->m_pkthdr.len = total_len; }#else m->m_pkthdr.len = m->m_len = total_len;#endif ifp->if_ipackets++; eh = mtod(m, struct ether_header *); m->m_pkthdr.rcvif = ifp;#if NBPFILTER > 0 /* * Handle BPF listeners. Let the BPF user see the packet, but * don't pass it up to the ether_input() layer unless it's * a broadcast packet, multicast packet, matches our ethernet * address or the interface is in promiscuous mode. */ if (ifp->if_bpf) { bpf_mtap(ifp, m); if (ifp->if_flags & IFF_PROMISC && (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) { m_freem(m); continue; } }#endif /* Remove header from mbuf and pass it on. */ m_adj(m, sizeof(struct ether_header)); ether_input(ifp, eh, m); } return;}void pn_rxeoc(sc) struct pn_softc *sc;{ pn_rxeof(sc); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON); CSR_WRITE_4(sc, PN_RXADDR, vtophys(sc->pn_cdata.pn_rx_head->pn_ptr)); PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON); CSR_WRITE_4(sc, PN_RXSTART, 0xFFFFFFFF); return;}/* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */static void pn_txeof(sc) struct pn_softc *sc;{ struct pn_chain *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* Clear the timeout timer. */ ifp->if_timer = 0; if (sc->pn_cdata.pn_tx_head == NULL) return; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ while(sc->pn_cdata.pn_tx_head->pn_mbuf != NULL) { u_int32_t txstat; cur_tx = sc->pn_cdata.pn_tx_head; txstat = PN_TXSTATUS(cur_tx); if (txstat & PN_TXSTAT_OWN) break; if (txstat & PN_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & PN_TXSTAT_EXCESSCOLL) ifp->if_collisions++; if (txstat & PN_TXSTAT_LATECOLL) ifp->if_collisions++; } ifp->if_collisions += (txstat & PN_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; m_freem(cur_tx->pn_mbuf); cur_tx->pn_mbuf = NULL; if (sc->pn_cdata.pn_tx_head == sc->pn_cdata.pn_tx_tail) { sc->pn_cdata.pn_tx_head = NULL; sc->pn_cdata.pn_tx_tail = NULL; break; } sc->pn_cdata.pn_tx_head = cur_tx->pn_nextdesc; } return;}/* * TX 'end of channel' interrupt handler. */static void pn_txeoc(sc) struct pn_softc *sc;{ struct ifnet *ifp; ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; if (sc->pn_cdata.pn_tx_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; sc->pn_cdata.pn_tx_tail = NULL; if (sc->pn_want_auto) { if (sc->pn_pinfo == NULL) pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); else pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); } } return;}static void pn_intr(arg) void *arg;{ struct pn_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; ifp = &sc->arpcom.ac_if; /* Supress unwanted interrupts. */ if (!(ifp->if_flags & IFF_UP)) { pn_stop(sc);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?