if_mx.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,495 行 · 第 1/4 页
C
2,495 行
* The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported' * bit cleared in the status register, but has the 'autoneg enabled' * bit set in the control register. This is a contradiction, and * I'm not sure how to handle it. If you want to force an attempt * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR * and see what happens. */#ifndef FORCE_AUTONEG_TFOUR /* * First, see if autoneg is supported. If not, there's * no point in continuing. */ phy_sts = mx_phy_readreg(sc, PHY_BMSR); if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { if (verbose) printf("mx%d: autonegotiation not supported\n", sc->mx_unit); ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; return; }#endif switch (flag) { case MX_FLAG_FORCEDELAY: /* * XXX Never use this option anywhere but in the probe * routine: making the kernel stop dead in its tracks * for three whole seconds after we've gone multi-user * is really bad manners. */ mx_autoneg_xmit(sc); DELAY(5000000); break; case MX_FLAG_SCHEDDELAY: /* * Wait for the transmitter to go idle before starting * an autoneg session, otherwise mx_start() may clobber * our timeout, and we don't want to allow transmission * during an autoneg session since that can screw it up. */ if (sc->mx_cdata.mx_tx_head != NULL) { sc->mx_want_auto = 1; return; } mx_autoneg_xmit(sc); ifp->if_timer = 5; sc->mx_autoneg = 1; sc->mx_want_auto = 0; return; break; case MX_FLAG_DELAYTIMEO: ifp->if_timer = 0; sc->mx_autoneg = 0; break; default: printf("mx%d: invalid autoneg flag: %d\n", sc->mx_unit, flag); return; } if (mx_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { if (verbose) printf("mx%d: autoneg complete, ", sc->mx_unit); phy_sts = mx_phy_readreg(sc, PHY_BMSR); } else { if (verbose) printf("mx%d: autoneg not complete, ", sc->mx_unit); } media = mx_phy_readreg(sc, PHY_BMCR); /* Link is good. Report modes and set duplex mode. */ if (mx_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { if (verbose) printf("link status good "); advert = mx_phy_readreg(sc, PHY_ANAR); ability = mx_phy_readreg(sc, PHY_LPAR); if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { ifm->ifm_media = IFM_ETHER|IFM_100_T4; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(100baseT4)\n"); } else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; media |= PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; media &= ~PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 10Mbps)\n"); } else if (advert & PHY_ANAR_10BTHALF && ability & PHY_ANAR_10BTHALF) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media &= ~PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 10Mbps)\n"); } media &= ~PHY_BMCR_AUTONEGENBL; /* Set ASIC's duplex mode to match the PHY. */ mx_setcfg(sc, media); mx_phy_writereg(sc, PHY_BMCR, media); } else { if (verbose) printf("no carrier\n"); } mx_init(sc); if (sc->mx_tx_pend) { sc->mx_autoneg = 0; sc->mx_tx_pend = 0; mx_start(ifp); } return;}/* * Invoke autoneg using internal NWAY. */static void mx_autoneg(sc, flag, verbose) struct mx_softc *sc; int flag; int verbose;{ u_int32_t media, ability; struct ifnet *ifp; struct ifmedia *ifm; ifm = &sc->ifmedia; ifp = &sc->arpcom.ac_if; ifm->ifm_media = IFM_ETHER | IFM_AUTO; switch (flag) { case MX_FLAG_FORCEDELAY: /* * XXX Never use this option anywhere but in the probe * routine: making the kernel stop dead in its tracks * for three whole seconds after we've gone multi-user * is really bad manners. */ MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX); MX_SETBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL); MX_SETBIT(sc, MX_10BTCTRL, MX_ASTAT_TXDISABLE); DELAY(5000000); break; case MX_FLAG_SCHEDDELAY: /* * Wait for the transmitter to go idle before starting * an autoneg session, otherwise mx_start() may clobber * our timeout, and we don't want to allow transmission * during an autoneg session since that can screw it up. */ if (sc->mx_cdata.mx_tx_head != NULL) { sc->mx_want_auto = 1; return; } MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX); MX_SETBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL); MX_SETBIT(sc, MX_10BTCTRL, MX_ASTAT_TXDISABLE); ifp->if_timer = 5; sc->mx_autoneg = 1; sc->mx_want_auto = 0; return; break; case MX_FLAG_DELAYTIMEO: ifp->if_timer = 0; sc->mx_autoneg = 0; break; default: printf("mx%d: invalid autoneg flag: %d\n", sc->mx_unit, flag); return; } if ((CSR_READ_4(sc, MX_10BTSTAT) & MX_TSTAT_ANEGSTAT) == MX_ASTAT_AUTONEGCMP) { if (verbose) printf("mx%d: autoneg complete, ", sc->mx_unit); } else { if (verbose) printf("mx%d: autoneg not complete, ", sc->mx_unit); } media = CSR_READ_4(sc, MX_NETCFG); /* Link is good. Report modes and set duplex mode. */ if (!(CSR_READ_4(sc, MX_10BTSTAT) & MX_TSTAT_LS10) || !(CSR_READ_4(sc, MX_10BTSTAT) & MX_TSTAT_LS100)) { if (verbose) printf("link status good "); ability = CSR_READ_4(sc, MX_NWAYSTAT); if (ability & MX_NWAY_100BT4) { ifm->ifm_media = IFM_ETHER|IFM_100_T4; media |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS| MX_NETCFG_SCRAMBLER; media &= ~(MX_NETCFG_FULLDUPLEX|MX_NETCFG_SPEEDSEL); printf("(100baseT4)\n"); } else if (ability & MX_NWAY_100BTFULL) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; media |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS| MX_NETCFG_SCRAMBLER; media |= MX_NETCFG_FULLDUPLEX; media &= ~MX_NETCFG_SPEEDSEL; printf("(full-duplex, 100Mbps)\n"); } else if (ability & MX_NWAY_100BTHALF) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; media |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS| MX_NETCFG_SCRAMBLER; media &= ~(MX_NETCFG_FULLDUPLEX|MX_NETCFG_SPEEDSEL); printf("(half-duplex, 100Mbps)\n"); } else if (ability & MX_NWAY_10BTFULL) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; media &= ~MX_NETCFG_PORTSEL; media |= (MX_NETCFG_FULLDUPLEX|MX_NETCFG_SPEEDSEL); printf("(full-duplex, 10Mbps)\n"); } else { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media &= ~MX_NETCFG_PORTSEL; media &= ~MX_NETCFG_FULLDUPLEX; media |= MX_NETCFG_SPEEDSEL; printf("(half-duplex, 10Mbps)\n"); } CSR_WRITE_4(sc, MX_NETCFG, media); MX_CLRBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL); } else { if (verbose) printf("no carrier\n"); } mx_init(sc); if (sc->mx_tx_pend) { sc->mx_autoneg = 0; sc->mx_tx_pend = 0; mx_start(ifp); } return;}static void mx_getmode_mii(sc) struct mx_softc *sc;{ u_int16_t bmsr; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; bmsr = mx_phy_readreg(sc, PHY_BMSR); if (bootverbose) printf("mx%d: PHY status word: %x\n", sc->mx_unit, bmsr); /* fallback */ sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; if (bmsr & PHY_BMSR_10BTHALF) { if (bootverbose) printf("mx%d: 10Mbps half-duplex mode supported\n", sc->mx_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); } if (bmsr & PHY_BMSR_10BTFULL) { if (bootverbose) printf("mx%d: 10Mbps full-duplex mode supported\n", sc->mx_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; } if (bmsr & PHY_BMSR_100BTXHALF) { if (bootverbose) printf("mx%d: 100Mbps half-duplex mode supported\n", sc->mx_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; } if (bmsr & PHY_BMSR_100BTXFULL) { if (bootverbose) printf("mx%d: 100Mbps full-duplex mode supported\n", sc->mx_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; } /* Some also support 100BaseT4. */ if (bmsr & PHY_BMSR_100BT4) { if (bootverbose) printf("mx%d: 100baseT4 mode supported\n", sc->mx_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4;#ifdef FORCE_AUTONEG_TFOUR if (bootverbose) printf("mx%d: forcing on autoneg support for BT4\n", sc->mx_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL): sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO;#endif } if (bmsr & PHY_BMSR_CANAUTONEG) { if (bootverbose) printf("mx%d: autoneg supported\n", sc->mx_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; } return;}/* * Set speed and duplex mode. */static void mx_setmode_mii(sc, media) struct mx_softc *sc; int media;{ u_int16_t bmcr; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * If an autoneg session is in progress, stop it. */ if (sc->mx_autoneg) { printf("mx%d: canceling autoneg session\n", sc->mx_unit); ifp->if_timer = sc->mx_autoneg = sc->mx_want_auto = 0; bmcr = mx_phy_readreg(sc, PHY_BMCR); bmcr &= ~PHY_BMCR_AUTONEGENBL; mx_phy_writereg(sc, PHY_BMCR, bmcr); } printf("mx%d: selecting MII, ", sc->mx_unit); bmcr = mx_phy_readreg(sc, PHY_BMCR); bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); if (IFM_SUBTYPE(media) == IFM_100_T4) { printf("100Mbps/T4, half-duplex\n"); bmcr |= PHY_BMCR_SPEEDSEL; bmcr &= ~PHY_BMCR_DUPLEX; } if (IFM_SUBTYPE(media) == IFM_100_TX) { printf("100Mbps, "); bmcr |= PHY_BMCR_SPEEDSEL; } if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10Mbps, "); bmcr &= ~PHY_BMCR_SPEEDSEL; } if ((media & IFM_GMASK) == IFM_FDX) { printf("full duplex\n"); bmcr |= PHY_BMCR_DUPLEX; } else { printf("half duplex\n"); bmcr &= ~PHY_BMCR_DUPLEX; } mx_setcfg(sc, bmcr); mx_phy_writereg(sc, PHY_BMCR, bmcr); return;}/* * Set speed and duplex mode on internal transceiver. */static void mx_setmode(sc, media, verbose) struct mx_softc *sc; int media; int verbose;{ struct ifnet *ifp; u_int32_t mode; ifp = &sc->arpcom.ac_if; /* * If an autoneg session is in progress, stop it. */ if (sc->mx_autoneg) { printf("mx%d: canceling autoneg session\n", sc->mx_unit); ifp->if_timer = sc->mx_autoneg = sc->mx_want_auto = 0; MX_CLRBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL); } if (verbose) printf("mx%d: selecting NWAY, ", sc->mx_unit); mode = CSR_READ_4(sc, MX_NETCFG); mode &= ~(MX_NETCFG_FULLDUPLEX|MX_NETCFG_PORTSEL| MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER|MX_NETCFG_SPEEDSEL); if (IFM_SUBTYPE(media) == IFM_100_T4) { if (verbose) printf("100Mbps/T4, half-duplex\n"); mode |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER; } if (IFM_SUBTYPE(media) == IFM_100_TX) { if (verbose) printf("100Mbps, "); mode |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER; } if (IFM_SUBTYPE(media) == IFM_10_T) { if (verbose) printf("10Mbps, "); mode &= ~MX_NETCFG_PORTSEL; mode |= MX_NETCFG_SPEEDSEL; } if ((media & IFM_GMASK) == IFM_FDX) { if (verbose) printf("full duplex\n"); mode |= MX_NETCFG_FULLDUPLEX; } else { if (verbose) printf("half duplex\n"); mode &= ~MX_NETCFG_FULLDUPLEX; } CSR_WRITE_4(sc, MX_NETCFG, mode); return;}/* * Programming the receiver filter on the tulip/PMAC is gross. You * have to construct a special setup frame and download it to the * chip via the transmit DMA engine. This routine is also somewhat * gross, as the setup frame is sent synchronously rather than putting * on the transmit queue. The transmitter has to be stopped, then we * can download the frame and wait for the 'owned' bit to clear. * * We always program the chip using 'hash perfect' mode, i.e. one perfect * address (our node address) and a 512-bit hash filter for multicast * frames. We also sneak the broadcast address into the hash filter since * we need that too. */void mx_setfilt(sc) struct mx_softc *sc;{ struct mx_desc *sframe; u_int32_t h, *sp; struct ifmultiaddr *ifma; struct ifnet *ifp; int i; ifp = &sc->arpcom.ac_if; MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON); MX_SETBIT(sc, MX_ISR, MX_ISR_TX_IDLE); sframe = &sc->mx_cdata.mx_sframe; sp = (u_int32_t *)&sc->mx_cdata.mx_sbuf; bzero((char *)sp, MX_SFRAME_LEN); sframe->mx_next = vtophys(&sc->mx_ldata->mx_tx_list[0]); sframe->mx_data = vtophys(&sc->mx_cdata.mx_sbuf); sframe->mx_ctl = MX_SFRAME_LEN | MX_TXCTL_TLINK | MX_TXCTL_SETUP | MX_FILTER_HASHPERF; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC); else MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_ALLMULTI); for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = mx_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sp[h >> 4] |= 1 << (h & 0xF); } if (ifp->if_flags & IFF_BROADCAST) { h = mx_calchash((caddr_t)ðerbroadcastaddr); sp[h >> 4] |= 1 << (h & 0xF); } sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; CSR_WRITE_4(sc, MX_TXADDR, vtophys(sframe)); MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON); sframe->mx_status = MX_TXSTAT_OWN; CSR_WRITE_4(sc, MX_TXSTART, 0xFFFFFFFF); /* * Wait for chip to clear the 'own' bit. */ for (i = 0; i < MX_TIMEOUT; i++) { DELAY(10); if (sframe->mx_status != MX_TXSTAT_OWN) break; } if (i == MX_TIMEOUT) printf("mx%d: failed to send setup frame\n", sc->mx_unit); MX_SETBIT(sc, MX_ISR, MX_ISR_TX_NOBUF|MX_ISR_TX_IDLE); return;}/* * In order to fiddle with the * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */static void mx_setcfg(sc, bmcr) struct mx_softc *sc; int bmcr;{ int i, restart = 0; if (CSR_READ_4(sc, MX_NETCFG) & (MX_NETCFG_TX_ON|MX_NETCFG_RX_ON)) { restart = 1; MX_CLRBIT(sc, MX_NETCFG, (MX_NETCFG_TX_ON|MX_NETCFG_RX_ON)); for (i = 0; i < MX_TIMEOUT; i++) { DELAY(10); if (CSR_READ_4(sc, MX_ISR) & MX_ISR_TX_IDLE) break; } if (i == MX_TIMEOUT) printf("mx%d: failed to force tx and " "rx to idle state\n", sc->mx_unit); } if (bmcr & PHY_BMCR_SPEEDSEL) { MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_SPEEDSEL); if (sc->mx_phy_addr == 0) { MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL| MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER); } } else MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_SPEEDSEL); if (bmcr & PHY_BMCR_DUPLEX) MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX); else MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX); if (restart) MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON|MX_NETCFG_RX_ON); return;}static void mx_reset(sc) struct mx_softc *sc;{ register int i; MX_SETBIT(sc, MX_BUSCTL, MX_BUSCTL_RESET); for (i = 0; i < MX_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, MX_BUSCTL) & MX_BUSCTL_RESET)) break; } if (i == MX_TIMEOUT) printf("mx%d: reset never completed!\n", sc->mx_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return;}/* * Probe for a Macronix PMAC chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. * We do a little bit of extra work to identify the exact type of * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, * but different revision IDs. The same is true for 98715/98715A * chips and the 98725. This doesn't affect a whole lot, but it * lets us tell the user exactly what type of device they have * in the probe output. */static const char *mx_probe(config_id, device_id) pcici_t config_id;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?