if_tl.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,302 行 · 第 1/5 页
C
2,302 行
return; } /* * Read the BMSR register twice: the LINKSTAT bit is a * latching bit. */ tl_phy_readreg(sc, PHY_BMSR); phy_sts = tl_phy_readreg(sc, PHY_BMSR); if (phy_sts & PHY_BMSR_AUTONEGCOMP) { if (verbose) printf("tl%d: autoneg complete, ", sc->tl_unit); phy_sts = tl_phy_readreg(sc, PHY_BMSR); } else { if (verbose) printf("tl%d: autoneg not complete, ", sc->tl_unit); } /* Link is good. Report modes and set duplex mode. */ if (phy_sts & PHY_BMSR_LINKSTAT) { if (verbose) printf("link status good "); advert = tl_phy_readreg(sc, TL_PHY_ANAR); ability = tl_phy_readreg(sc, TL_PHY_LPAR); media = tl_phy_readreg(sc, PHY_BMCR); /* * Be sure to turn off the ISOLATE and * LOOPBACK bits in the control register, * otherwise we may not be able to communicate. */ media &= ~(PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE); /* Set the DUPLEX bit in the NetCmd register accordingly. */ 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; if (verbose) 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; if (verbose) 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; if (verbose) 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; if (verbose) printf("(full-duplex, 10Mbps)\n"); } else { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media &= ~PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; if (verbose) printf("(half-duplex, 10Mbps)\n"); } if (media & PHY_BMCR_DUPLEX) tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); else tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); media &= ~PHY_BMCR_AUTONEGENBL; tl_phy_writereg(sc, PHY_BMCR, media); } else { if (verbose) printf("no carrier\n"); } tl_init(sc); if (sc->tl_tx_pend) { sc->tl_autoneg = 0; sc->tl_tx_pend = 0; tl_start(ifp); } return;}/* * Set speed and duplex mode. Also program autoneg advertisements * accordingly. */static void tl_setmode(sc, media) struct tl_softc *sc; int media;{ u_int16_t bmcr; if (sc->tl_bitrate) { if (IFM_SUBTYPE(media) == IFM_10_5) tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); if (IFM_SUBTYPE(media) == IFM_10_T) { tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); if ((media & IFM_GMASK) == IFM_FDX) { tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); } else { tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); } } return; } bmcr = tl_phy_readreg(sc, PHY_BMCR); bmcr &= ~(PHY_BMCR_SPEEDSEL|PHY_BMCR_DUPLEX|PHY_BMCR_AUTONEGENBL| PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE); if (IFM_SUBTYPE(media) == IFM_LOOP) bmcr |= PHY_BMCR_LOOPBK; if (IFM_SUBTYPE(media) == IFM_AUTO) bmcr |= PHY_BMCR_AUTONEGENBL; /* * The ThunderLAN's internal PHY has an AUI transceiver * that can be selected. This is usually attached to a * 10base2/BNC port. In order to activate this port, we * have to set the AUISEL bit in the internal PHY's * special control register. */ if (IFM_SUBTYPE(media) == IFM_10_5) { u_int16_t addr, ctl; addr = sc->tl_phy_addr; sc->tl_phy_addr = TL_PHYADDR_MAX; ctl = tl_phy_readreg(sc, TL_PHY_CTL); ctl |= PHY_CTL_AUISEL; tl_phy_writereg(sc, TL_PHY_CTL, ctl); tl_phy_writereg(sc, PHY_BMCR, bmcr); sc->tl_phy_addr = addr; bmcr |= PHY_BMCR_ISOLATE; } else { u_int16_t addr, ctl; addr = sc->tl_phy_addr; sc->tl_phy_addr = TL_PHYADDR_MAX; ctl = tl_phy_readreg(sc, TL_PHY_CTL); ctl &= ~PHY_CTL_AUISEL; tl_phy_writereg(sc, TL_PHY_CTL, ctl); tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_ISOLATE); sc->tl_phy_addr = addr; bmcr &= ~PHY_BMCR_ISOLATE; } if (IFM_SUBTYPE(media) == IFM_100_TX) { bmcr |= PHY_BMCR_SPEEDSEL; if ((media & IFM_GMASK) == IFM_FDX) { bmcr |= PHY_BMCR_DUPLEX; tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); } else { bmcr &= ~PHY_BMCR_DUPLEX; tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); } } if (IFM_SUBTYPE(media) == IFM_10_T) { bmcr &= ~PHY_BMCR_SPEEDSEL; if ((media & IFM_GMASK) == IFM_FDX) { bmcr |= PHY_BMCR_DUPLEX; tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); } else { bmcr &= ~PHY_BMCR_DUPLEX; tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); } } tl_phy_writereg(sc, PHY_BMCR, bmcr); tl_init(sc); return;}/* * Calculate the hash of a MAC address for programming the multicast hash * table. This hash is simply the address split into 6-bit chunks * XOR'd, e.g. * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then * the folded 24-bit value is split into 6-bit portions and XOR'd. */static int tl_calchash(addr) caddr_t addr;{ int t; t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | (addr[2] ^ addr[5]); return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f;}/* * The ThunderLAN has a perfect MAC address filter in addition to * the multicast hash filter. The perfect filter can be programmed * with up to four MAC addresses. The first one is always used to * hold the station address, which leaves us free to use the other * three for multicast addresses. */static void tl_setfilt(sc, addr, slot) struct tl_softc *sc; caddr_t addr; int slot;{ int i; u_int16_t regaddr; regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); for (i = 0; i < ETHER_ADDR_LEN; i++) tl_dio_write8(sc, regaddr + i, *(addr + i)); return;}/* * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly * linked list. This is fine, except addresses are added from the head * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") * group to always be in the perfect filter, but as more groups are added, * the 224.0.0.1 entry (which is always added first) gets pushed down * the list and ends up at the tail. So after 3 or 4 multicast groups * are added, the all-hosts entry gets pushed out of the perfect filter * and into the hash table. * * Because the multicast list is a doubly-linked list as opposed to a * circular queue, we don't have the ability to just grab the tail of * the list and traverse it backwards. Instead, we have to traverse * the list once to find the tail, then traverse it again backwards to * update the multicast filter. */static void tl_setmulti(sc) struct tl_softc *sc;{ struct ifnet *ifp; u_int32_t hashes[2] = { 0, 0 }; int h, i; struct ifmultiaddr *ifma; u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; ifp = &sc->arpcom.ac_if; /* First, zot all the existing filters. */ for (i = 1; i < 4; i++) tl_setfilt(sc, (caddr_t)&dummy, i); tl_dio_write32(sc, TL_HASH1, 0); tl_dio_write32(sc, TL_HASH2, 0); /* Now program new ones. */ if (ifp->if_flags & IFF_ALLMULTI) { hashes[0] = 0xFFFFFFFF; hashes[1] = 0xFFFFFFFF; } else { i = 1; /* First find the tail of the list. */ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_link.le_next == NULL) break; } /* Now traverse the list backwards. */ for (; ifma != NULL && ifma != (void *)&ifp->if_multiaddrs; ifma = (struct ifmultiaddr *)ifma->ifma_link.le_prev) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* * Program the first three multicast groups * into the perfect filter. For all others, * use the hash table. */ if (i < 4) { tl_setfilt(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); i++; continue; } h = tl_calchash( LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } } tl_dio_write32(sc, TL_HASH1, hashes[0]); tl_dio_write32(sc, TL_HASH2, hashes[1]); return;}/* * This routine is recommended by the ThunderLAN manual to insure that * the internal PHY is powered up correctly. It also recommends a one * second pause at the end to 'wait for the clocks to start' but in my * experience this isn't necessary. */static void tl_hardreset(sc) struct tl_softc *sc;{ int i; u_int16_t old_addr, flags; old_addr = sc->tl_phy_addr; for (i = 0; i < TL_PHYADDR_MAX + 1; i++) { sc->tl_phy_addr = i; tl_mii_sync(sc); } flags = PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE|PHY_BMCR_PWRDOWN; for (i = 0; i < TL_PHYADDR_MAX + 1; i++) { sc->tl_phy_addr = i; tl_phy_writereg(sc, PHY_BMCR, flags); } sc->tl_phy_addr = TL_PHYADDR_MAX; tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_ISOLATE); DELAY(50000); tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE); tl_mii_sync(sc); while(tl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); sc->tl_phy_addr = old_addr; return;}static void tl_softreset(sc, internal) struct tl_softc *sc; int internal;{ u_int32_t cmd, dummy, i; /* Assert the adapter reset bit. */ CMD_SET(sc, TL_CMD_ADRST); /* Turn off interrupts */ CMD_SET(sc, TL_CMD_INTSOFF); /* First, clear the stats registers. */ for (i = 0; i < 5; i++) dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); /* Clear Areg and Hash registers */ for (i = 0; i < 8; i++) tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); /* * Set up Netconfig register. Enable one channel and * one fragment mode. */ tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); if (internal && !sc->tl_bitrate) { tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); } else { tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); } /* Handle cards with bitrate devices. */ if (sc->tl_bitrate) tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); /* Set PCI burst size */ tl_dio_write8(sc, TL_BSIZEREG, 0x33); /* * Load adapter irq pacing timer and tx threshold. * We make the transmit threshold 1 initially but we may * change that later. */ cmd = CSR_READ_4(sc, TL_HOSTCMD); cmd |= TL_CMD_NES; cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); /* Unreset the MII */ tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); /* Clear status register */ tl_dio_setbit16(sc, TL_NETSTS, TL_STS_MIRQ); tl_dio_setbit16(sc, TL_NETSTS, TL_STS_HBEAT); tl_dio_setbit16(sc, TL_NETSTS, TL_STS_TXSTOP); tl_dio_setbit16(sc, TL_NETSTS, TL_STS_RXSTOP); /* Enable network status interrupts for everything. */ tl_dio_setbit(sc, TL_NETMASK, TL_MASK_MASK7|TL_MASK_MASK6| TL_MASK_MASK5|TL_MASK_MASK4); /* Take the adapter out of reset */ tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); /* Wait for things to settle down a little. */ DELAY(500); return;}/* * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs * against our list and return its name if we find a match. */static const char *tl_probe(config_id, device_id) pcici_t config_id; pcidi_t device_id;{ struct tl_type *t; t = tl_devs; while(t->tl_name != NULL) { if ((device_id & 0xFFFF) == t->tl_vid && ((device_id >> 16) & 0xFFFF) == t->tl_did) return(t->tl_name); t++; } return(NULL);}/* * Do the interface setup and attach for a PHY on a particular * ThunderLAN chip. Also also set up interrupt vectors. */ static int tl_attach_phy(sc) struct tl_softc *sc;{ int phy_ctl; struct tl_type *p = tl_phys; int media = IFM_ETHER|IFM_100_TX|IFM_FDX; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; sc->tl_phy_did = tl_phy_readreg(sc, TL_PHY_DEVID); sc->tl_phy_vid = tl_phy_readreg(sc, TL_PHY_VENID); sc->tl_phy_sts = tl_phy_readreg(sc, TL_PHY_GENSTS); phy_ctl = tl_phy_readreg(sc, TL_PHY_GENCTL); /* * PHY revision numbers tend to vary a bit. Our algorithm here
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?