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 + -
显示快捷键?