if_rl.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,918 行 · 第 1/3 页

C
1,918
字号
		return;	}	/* first, zot all the existing hash bits */	CSR_WRITE_4(sc, RL_MAR0, 0);	CSR_WRITE_4(sc, RL_MAR4, 0);	/* now program new ones */	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 = rl_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));		if (h < 32)			hashes[0] |= (1 << h);		else			hashes[1] |= (1 << (h - 32));		mcnt++;	}	if (mcnt)		rxfilt |= RL_RXCFG_RX_MULTI;	else		rxfilt &= ~RL_RXCFG_RX_MULTI;	CSR_WRITE_4(sc, RL_RXCFG, rxfilt);	CSR_WRITE_4(sc, RL_MAR0, hashes[0]);	CSR_WRITE_4(sc, RL_MAR4, hashes[1]);	return;}/* * Initiate an autonegotiation session. */static void rl_autoneg_xmit(sc)	struct rl_softc		*sc;{	u_int16_t		phy_sts;	rl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);	DELAY(500);	while(rl_phy_readreg(sc, PHY_BMCR)			& PHY_BMCR_RESET);	phy_sts = rl_phy_readreg(sc, PHY_BMCR);	phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;	rl_phy_writereg(sc, PHY_BMCR, phy_sts);	return;}/* * Invoke autonegotiation on a PHY. Also used with the 8139 internal * transceiver. */static void rl_autoneg_mii(sc, flag, verbose)	struct rl_softc		*sc;	int			flag;	int			verbose;{	u_int16_t		phy_sts = 0, media, advert, ability;	struct ifnet		*ifp;	struct ifmedia		*ifm;	ifm = &sc->ifmedia;	ifp = &sc->arpcom.ac_if;	/*	 * The 100baseT4 PHY sometimes 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 = rl_phy_readreg(sc, PHY_BMSR);	if (!(phy_sts & PHY_BMSR_CANAUTONEG)) {		if (verbose)			printf("rl%d: autonegotiation not supported\n",							sc->rl_unit);		return;	}#endif	switch (flag) {	case RL_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.	 	 */		rl_autoneg_xmit(sc);		DELAY(5000000);		break;	case RL_FLAG_SCHEDDELAY:		/*		 * Wait for the transmitter to go idle before starting		 * an autoneg session, otherwise rl_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->rl_cdata.last_tx != sc->rl_cdata.cur_tx) {			sc->rl_want_auto = 1;			return;		}		rl_autoneg_xmit(sc);		ifp->if_timer = 5;		sc->rl_autoneg = 1;		sc->rl_want_auto = 0;		return;		break;	case RL_FLAG_DELAYTIMEO:		ifp->if_timer = 0;		sc->rl_autoneg = 0;		break;	default:		printf("rl%d: invalid autoneg flag: %d\n", sc->rl_unit, flag);		return;	}	if (rl_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) {		if (verbose)			printf("rl%d: autoneg complete, ", sc->rl_unit);		phy_sts = rl_phy_readreg(sc, PHY_BMSR);	} else {		if (verbose)			printf("rl%d: autoneg not complete, ", sc->rl_unit);	}	media = rl_phy_readreg(sc, PHY_BMCR);	/* Link is good. Report modes and set duplex mode. */	if (rl_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) {		if (verbose)			printf("link status good ");		advert = rl_phy_readreg(sc, PHY_ANAR);		ability = rl_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 {			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;			media &= ~PHY_BMCR_SPEEDSEL;			media &= ~PHY_BMCR_DUPLEX;			printf("(half-duplex, 10Mbps)\n");		}		/* Set ASIC's duplex mode to match the PHY. */		rl_phy_writereg(sc, PHY_BMCR, media);	} else {		if (verbose)			printf("no carrier\n");	}	rl_init(sc);	if (sc->rl_tx_pend) {		sc->rl_autoneg = 0;		sc->rl_tx_pend = 0;		rl_start(ifp);	}	return;}static void rl_getmode_mii(sc)	struct rl_softc		*sc;{	u_int16_t		bmsr;	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	bmsr = rl_phy_readreg(sc, PHY_BMSR);	if (bootverbose)		printf("rl%d: PHY status word: %x\n", sc->rl_unit, bmsr);	/* fallback */	sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;	if (bmsr & PHY_BMSR_10BTHALF) {		if (bootverbose)			printf("rl%d: 10Mbps half-duplex mode supported\n",								sc->rl_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("rl%d: 10Mbps full-duplex mode supported\n",								sc->rl_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("rl%d: 100Mbps half-duplex mode supported\n",								sc->rl_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("rl%d: 100Mbps full-duplex mode supported\n",								sc->rl_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("rl%d: 100baseT4 mode supported\n", sc->rl_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("rl%d: forcing on autoneg support for BT4\n",							 sc->rl_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("rl%d: autoneg supported\n", sc->rl_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 rl_setmode_mii(sc, media)	struct rl_softc		*sc;	int			media;{	u_int16_t		bmcr;	printf("rl%d: selecting MII, ", sc->rl_unit);	bmcr = rl_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;	}	rl_phy_writereg(sc, PHY_BMCR, bmcr);	return;}static void rl_reset(sc)	struct rl_softc		*sc;{	register int		i;	CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET);	for (i = 0; i < RL_TIMEOUT; i++) {		DELAY(10);		if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET))			break;	}	if (i == RL_TIMEOUT)		printf("rl%d: reset never completed!\n", sc->rl_unit);        return;}/* * Probe for a RealTek 8129/8139 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */static const char *rl_probe(config_id, device_id)	pcici_t			config_id;	pcidi_t			device_id;{	struct rl_type		*t;	t = rl_devs;	while(t->rl_name != NULL) {		if ((device_id & 0xFFFF) == t->rl_vid &&		    ((device_id >> 16) & 0xFFFF) == t->rl_did) {			return(t->rl_name);		}		t++;	}	return(NULL);}/* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */static voidrl_attach(config_id, unit)	pcici_t			config_id;	int			unit;{	int			s, i;#ifndef RL_USEIOSPACE	vm_offset_t		pbase, vbase;#endif	u_char			eaddr[ETHER_ADDR_LEN];	u_int32_t		command;	struct rl_softc		*sc;	struct ifnet		*ifp;	int			media = IFM_ETHER|IFM_100_TX|IFM_FDX;	struct rl_type		*p;	u_int16_t		phy_vid, phy_did, phy_sts;	u_int16_t		rl_did = 0;	s = splimp();	sc = malloc(sizeof(struct rl_softc), M_DEVBUF, M_NOWAIT);	if (sc == NULL) {		printf("rl%d: no memory for softc struct!\n", unit);		goto fail;	}	bzero(sc, sizeof(struct rl_softc));	/*	 * Handle power management nonsense.	 */	command = pci_conf_read(config_id, RL_PCI_CAPID) & 0x000000FF;	if (command == 0x01) {		command = pci_conf_read(config_id, RL_PCI_PWRMGMTCTRL);		if (command & RL_PSTATE_MASK) {			u_int32_t		iobase, membase, irq;			/* Save important PCI config data. */			iobase = pci_conf_read(config_id, RL_PCI_LOIO);			membase = pci_conf_read(config_id, RL_PCI_LOMEM);			irq = pci_conf_read(config_id, RL_PCI_INTLINE);			/* Reset the power state. */			printf("rl%d: chip is is in D%d power mode "			"-- setting to D0\n", unit, command & RL_PSTATE_MASK);			command &= 0xFFFFFFFC;			pci_conf_write(config_id, RL_PCI_PWRMGMTCTRL, command);			/* Restore PCI config data. */			pci_conf_write(config_id, RL_PCI_LOIO, iobase);			pci_conf_write(config_id, RL_PCI_LOMEM, membase);			pci_conf_write(config_id, RL_PCI_INTLINE, irq);		}	}	/*	 * Map control/status registers.	 */	command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);	command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);	pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, command);	command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);#ifdef RL_USEIOSPACE	if (!(command & PCIM_CMD_PORTEN)) {		printf("rl%d: failed to enable I/O ports!\n", unit);		free(sc, M_DEVBUF);		goto fail;	}	if (!pci_map_port(config_id, RL_PCI_LOIO,				(u_int16_t *)&(sc->rl_bhandle))) {		printf ("rl%d: couldn't map ports\n", unit);		goto fail;	}	sc->rl_btag = I386_BUS_SPACE_IO;#else	if (!(command & PCIM_CMD_MEMEN)) {		printf("rl%d: failed to enable memory mapping!\n", unit);		goto fail;	}	if (!pci_map_mem(config_id, RL_PCI_LOMEM, &vbase, &pbase)) {		printf ("rl%d: couldn't map memory\n", unit);		goto fail;	}	sc->rl_btag = I386_BUS_SPACE_MEM;	sc->rl_bhandle = vbase;#endif	/* Allocate interrupt */	if (!pci_map_int(config_id, rl_intr, sc, &net_imask)) {		printf("rl%d: couldn't map interrupt\n", unit);		goto fail;	}	/* Reset the adapter. */	rl_reset(sc);	/*	 * Get station address from the EEPROM.	 */	rl_read_eeprom(sc, (caddr_t)&eaddr, RL_EE_EADDR, 3, 0);	/*	 * A RealTek chip was detected. Inform the world.	 */	printf("rl%d: Ethernet address: %6D\n", unit, eaddr, ":");	sc->rl_unit = unit;	bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);	/*	 * Now read the exact device type from the EEPROM to find	 * out if it's an 8129 or 8139.	 */	rl_read_eeprom(sc, (caddr_t)&rl_did, RL_EE_PCI_DID, 1, 0);	if (rl_did == RT_DEVICEID_8139 || rl_did == ACCTON_DEVICEID_5030 ||		rl_did == DELTA_DEVICEID_8139)		sc->rl_type = RL_8139;	else if (rl_did == RT_DEVICEID_8129)		sc->rl_type = RL_8129;	else {		printf("rl%d: unknown device ID: %x\n", unit, rl_did);		free(sc, M_DEVBUF);		goto fail;	}	sc->rl_cdata.rl_rx_buf = contigmalloc(RL_RXBUFLEN + 16, M_DEVBUF,		M_NOWAIT, 0x100000, 0xffffffff, PAGE_SIZE, 0);	if (sc->rl_cdata.rl_rx_buf == NULL) {		free(sc, M_DEVBUF);		printf("rl%d: no memory for list buffers!\n", unit);		goto fail;	}	ifp = &sc->arpcom.ac_if;	ifp->if_softc = sc;	ifp->if_unit = unit;	ifp->if_name = "rl";	ifp->if_mtu = ETHERMTU;	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;	ifp->if_ioctl = rl_ioctl;	ifp->if_output = ether_output;	ifp->if_start = rl_start;	ifp->if_watchdog = rl_watchdog;	ifp->if_init = rl_init;	ifp->if_baudrate = 10000000;	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;	if (sc->rl_type == RL_8129) {		if (bootverbose)			printf("rl%d: probing for a PHY\n", sc->rl_unit);		for (i = RL_PHYADDR_MIN; i < RL_PHYADDR_MAX + 1; i++) {			if (bootverbose)				printf("rl%d: checking address: %d\n",							sc->rl_unit, i);			sc->rl_phy_addr = i;			rl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);			DELAY(500);			while(rl_phy_readreg(sc, PHY_BMCR)					& PHY_BMCR_RESET);			if ((phy_sts = rl_phy_readreg(sc, PHY_BMSR)))				break;		}		if (phy_sts) {			phy_vid = rl_phy_readreg(sc, PHY_VENID);			phy_did = rl_phy_readreg(sc, PHY_DEVID);			if (bootverbose)				printf("rl%d: found PHY at address %d, ",						sc->rl_unit, sc->rl_phy_addr);			if (bootverbose)				printf("vendor id: %x device id: %x\n",					phy_vid, phy_did);			p = rl_phys;			while(p->rl_vid) {				if (phy_vid == p->rl_vid &&					(phy_did | 0x000F) == p->rl_did) {					sc->rl_pinfo = p;					break;				}				p++;			}			if (sc->rl_pinfo == NULL)				sc->rl_pinfo = &rl_phys[PHY_UNKNOWN];			if (bootverbose)				printf("rl%d: PHY type: %s\n",					sc->rl_unit, sc->rl_pinfo->rl_name);		} else {			printf("rl%d: MII without any phy!\n", sc->rl_unit);		}	}	/*	 * Do ifmedia setup.	 */	ifmedia_init(&sc->ifmedia, 0, rl_ifmedia_upd, rl_ifmedia_sts);	rl_getmode_mii(sc);	/* Choose a default media. */	media = IFM_ETHER|IFM_AUTO;	ifmedia_set(&sc->ifmedia, media);	rl_autoneg_mii(sc, RL_FLAG_FORCEDELAY, 1);	/*	 * Call MI attach routines.	 */	if_attach(ifp);	ether_ifattach(ifp);#if NBPFILTER > 0	bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));#endif	at_shutdown(rl_shutdown, sc, SHUTDOWN_POST_SYNC);fail:	splx(s);	return;}/* * Initialize the transmit descriptors. */static int rl_list_tx_init(sc)	struct rl_softc		*sc;{	struct rl_chain_data	*cd;	int			i;	cd = &sc->rl_cdata;	for (i = 0; i < RL_TX_LIST_CNT; i++) {		cd->rl_tx_chain[i] = NULL;		CSR_WRITE_4(sc, RL_TXADDR0 + i, 0x0000000);	}	sc->rl_cdata.cur_tx = 0;	sc->rl_cdata.last_tx = 0;	return(0);}/* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. * * You know there's something wrong with a PCI bus-master chip design * when you have to use m_devget(). * * The receive operation is badly documented in the datasheet, so I'll * attempt to document it here. The driver provides a buffer area and * places its base address in the RX buffer start address register. * The chip then begins copying frames into the RX buffer. Each frame * is preceeded by a 32-bit RX status word which specifies the length * of the frame and certain other status bits. Each frame (starting with * the status word) is also 32-bit aligned. The frame length is in the * first 16 bits of the status word; the lower 15 bits correspond with * the 'rx status register' mentioned in the datasheet. */static void rl_rxeof(sc)	struct rl_softc		*sc;{        struct ether_header	*eh;        struct mbuf		*m;        struct ifnet		*ifp;	int			total_len = 0;	u_int32_t		rxstat;	caddr_t			rxbufpos;	int			wrap = 0;	u_int16_t		cur_rx;	u_int16_t		limit;	u_int16_t		rx_bytes = 0, max_bytes;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?