if_pn.c

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

C
2,280
字号
		PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB);		DELAY(5000000);		break;	case PN_FLAG_SCHEDDELAY:		/*		 * Wait for the transmitter to go idle before starting		 * an autoneg session, otherwise pn_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->pn_cdata.pn_tx_head != NULL) {			sc->pn_want_auto = 1;			return;		}		CSR_WRITE_4(sc, PN_GEN,		    PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP);		PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR);		PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB);		ifp->if_timer = 5;		sc->pn_autoneg = 1;		sc->pn_want_auto = 0;		return;		break;	case PN_FLAG_DELAYTIMEO:		ifp->if_timer = 0;		sc->pn_autoneg = 0;		break;	default:		printf("pn%d: invalid autoneg flag: %d\n", sc->pn_unit, flag);		return;	}	if (CSR_READ_4(sc, PN_NWAY) & PN_NWAY_LPAR) {		if (verbose)			printf("pn%d: autoneg complete, ", sc->pn_unit);	} else {		if (verbose)			printf("pn%d: autoneg not complete, ", sc->pn_unit);	}	/* Link is good. Report modes and set duplex mode. */	if (CSR_READ_4(sc, PN_ISR) & PN_ISR_LINKPASS) {		if (verbose)			printf("link status good ");		ability = CSR_READ_4(sc, PN_NWAY);		if (ability & PN_NWAY_LPAR100T4) {			ifm->ifm_media = IFM_ETHER|IFM_100_T4;			nway = PN_NWAY_MODE_100T4;			printf("(100baseT4)\n");		} else if (ability & PN_NWAY_LPAR100FULL) {			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX;			nway = PN_NWAY_MODE_100FD;			printf("(full-duplex, 100Mbps)\n");		} else if (ability & PN_NWAY_LPAR100HALF) {			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX;			nway = PN_NWAY_MODE_100HD;			printf("(half-duplex, 100Mbps)\n");		} else if (ability & PN_NWAY_LPAR10FULL) {			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX;			nway = PN_NWAY_MODE_10FD;			printf("(full-duplex, 10Mbps)\n");		} else if (ability & PN_NWAY_LPAR10HALF) {			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;			nway = PN_NWAY_MODE_10HD;			printf("(half-duplex, 10Mbps)\n");		}		/* Set ASIC's duplex mode to match the PHY. */		pn_setcfg(sc, ifm->ifm_media);		CSR_WRITE_4(sc, PN_NWAY, nway);	} else {		if (verbose)			printf("no carrier\n");	}	pn_init(sc);	if (sc->pn_tx_pend) {		sc->pn_autoneg = 0;		sc->pn_tx_pend = 0;		pn_start(ifp);	}	return;}static void pn_setmode(sc, media)	struct pn_softc		*sc;	int			media;{	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	/*	 * If an autoneg session is in progress, stop it.	 */	if (sc->pn_autoneg) {		printf("pn%d: canceling autoneg session\n", sc->pn_unit);		ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0;		PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR);	}	printf("pn%d: selecting NWAY, ", sc->pn_unit);	if (IFM_SUBTYPE(media) == IFM_100_T4) {		printf("100Mbps/T4, half-duplex\n");	}	if (IFM_SUBTYPE(media) == IFM_100_TX) {		printf("100Mbps, ");	}	if (IFM_SUBTYPE(media) == IFM_10_T) {		printf("10Mbps, ");	}	if ((media & IFM_GMASK) == IFM_FDX) {		printf("full duplex\n");	} else {		printf("half duplex\n");	}	pn_setcfg(sc, media);	return;}static void pn_setmode_mii(sc, media)	struct pn_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->pn_autoneg) {		printf("pn%d: canceling autoneg session\n", sc->pn_unit);		ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0;		bmcr = pn_phy_readreg(sc, PHY_BMCR);		bmcr &= ~PHY_BMCR_AUTONEGENBL;		pn_phy_writereg(sc, PHY_BMCR, bmcr);	}	printf("pn%d: selecting MII, ", sc->pn_unit);	bmcr = pn_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;	}	pn_setcfg(sc, media);	pn_phy_writereg(sc, PHY_BMCR, bmcr);	return;}/* * Programming the receiver filter on the tulip/PNIC 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 pn_setfilt(sc)	struct pn_softc		*sc;{	struct pn_desc		*sframe;	u_int32_t		h, *sp;	struct ifmultiaddr	*ifma;	struct ifnet		*ifp;	int			i;	ifp = &sc->arpcom.ac_if;	PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON);	PN_SETBIT(sc, PN_ISR, PN_ISR_TX_IDLE);	sframe = &sc->pn_cdata.pn_sframe;	sp = (u_int32_t *)&sc->pn_cdata.pn_sbuf;	bzero((char *)sp, PN_SFRAME_LEN);	sframe->pn_status = PN_TXSTAT_OWN;	sframe->pn_next = vtophys(&sc->pn_ldata->pn_tx_list[0]);	sframe->pn_data = vtophys(&sc->pn_cdata.pn_sbuf);	sframe->pn_ctl = PN_SFRAME_LEN | PN_TXCTL_TLINK |			PN_TXCTL_SETUP | PN_FILTER_HASHPERF;	/* If we want promiscuous mode, set the allframes bit. */	if (ifp->if_flags & IFF_PROMISC)		PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC);	else		PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC);	if (ifp->if_flags & IFF_ALLMULTI)		PN_SETBIT(sc, PN_NETCFG, PN_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 = pn_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));		sp[h >> 4] |= 1 << (h & 0xF);	}	if (ifp->if_flags & IFF_BROADCAST) {		h = pn_calchash(etherbroadcastaddr);		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, PN_TXADDR, vtophys(sframe));	PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON);	CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF);	/*	 * Wait for chip to clear the 'own' bit.	 */	for (i = 0; i < PN_TIMEOUT; i++) {		DELAY(10);		if (sframe->pn_status != PN_TXSTAT_OWN)			break;	}	if (i == PN_TIMEOUT)		printf("pn%d: failed to send setup frame\n", sc->pn_unit);	PN_SETBIT(sc, PN_ISR, PN_ISR_TX_NOBUF|PN_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 pn_setcfg(sc, media)	struct pn_softc		*sc;	u_int32_t		media;{	int			i, restart = 0;	if (CSR_READ_4(sc, PN_NETCFG) & (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON)) {		restart = 1;		PN_CLRBIT(sc, PN_NETCFG, (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON));		for (i = 0; i < PN_TIMEOUT; i++) {			DELAY(10);			if ((CSR_READ_4(sc, PN_ISR) & PN_ISR_TX_IDLE) &&				(CSR_READ_4(sc, PN_ISR) & PN_ISR_RX_IDLE))				break;		}		if (i == PN_TIMEOUT)			printf("pn%d: failed to force tx and "				"rx to idle state\n", sc->pn_unit);	}	if (IFM_SUBTYPE(media) == IFM_100_TX) {		PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL);		if (sc->pn_pinfo == NULL) {			CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE|			    PN_GEN_SPEEDSEL|PN_GEN_100TX_LOOP);			PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_PCS|			    PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB);			PN_SETBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL);		}	} else {		PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL);		if (sc->pn_pinfo == NULL) {			CSR_WRITE_4(sc, PN_GEN,			    PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP);			PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_PCS|			    PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB);			PN_CLRBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL);		}	}	if ((media & IFM_GMASK) == IFM_FDX) {		PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX);		if (sc->pn_pinfo == NULL)			PN_SETBIT(sc, PN_NWAY, PN_NWAY_DUPLEX);	} else {		PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX);		if (sc->pn_pinfo == NULL)			PN_CLRBIT(sc, PN_NWAY, PN_NWAY_DUPLEX);	}	if (restart)		PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON|PN_NETCFG_RX_ON);	return;}static void pn_reset(sc)	struct pn_softc		*sc;{	register int		i;	PN_SETBIT(sc, PN_BUSCTL, PN_BUSCTL_RESET);	for (i = 0; i < PN_TIMEOUT; i++) {		DELAY(10);		if (!(CSR_READ_4(sc, PN_BUSCTL) & PN_BUSCTL_RESET))			break;	}	if (i == PN_TIMEOUT)		printf("pn%d: reset never completed!\n", sc->pn_unit);	/* Wait a little while for the chip to get its brains in order. */	DELAY(1000);        return;}/* * Probe for a Lite-On PNIC chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */static const char *pn_probe(config_id, device_id)	pcici_t			config_id;	pcidi_t			device_id;{	struct pn_type		*t;	u_int32_t		rev;	t = pn_devs;	while(t->pn_name != NULL) {		if ((device_id & 0xFFFF) == t->pn_vid &&		    ((device_id >> 16) & 0xFFFF) == t->pn_did) {			if (t->pn_did == PN_DEVICEID_PNIC) {				rev = pci_conf_read(config_id,				    PN_PCI_REVISION) & 0xFF;				switch(rev & PN_REVMASK) {				case PN_REVID_82C168:					return(t->pn_name);					break;				case PN_REVID_82C169:					t++;					return(t->pn_name);				default:					printf("unknown PNIC rev: %x\n", rev);					break;				}			}			return(t->pn_name);		}		t++;	}	return(NULL);}/* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */static voidpn_attach(config_id, unit)	pcici_t			config_id;	int			unit;{	int			s, i;#ifndef PN_USEIOSPACE	vm_offset_t		pbase, vbase;#endif	u_char			eaddr[ETHER_ADDR_LEN];	u_int32_t		command;	struct pn_softc		*sc;	struct ifnet		*ifp;	int			media = IFM_ETHER|IFM_100_TX|IFM_FDX;	unsigned int		round;	caddr_t			roundptr;	struct pn_type		*p;	u_int16_t		phy_vid, phy_did, phy_sts;#ifdef PN_RX_BUG_WAR	u_int32_t		revision = 0;#endif	s = splimp();	sc = malloc(sizeof(struct pn_softc), M_DEVBUF, M_NOWAIT);	if (sc == NULL) {		printf("pn%d: no memory for softc struct!\n", unit);		return;	}	bzero(sc, sizeof(struct pn_softc));	/*	 * Handle power management nonsense.	 */	command = pci_conf_read(config_id, PN_PCI_CAPID) & 0x000000FF;	if (command == 0x01) {		command = pci_conf_read(config_id, PN_PCI_PWRMGMTCTRL);		if (command & PN_PSTATE_MASK) {			u_int32_t		iobase, membase, irq;			/* Save important PCI config data. */			iobase = pci_conf_read(config_id, PN_PCI_LOIO);			membase = pci_conf_read(config_id, PN_PCI_LOMEM);			irq = pci_conf_read(config_id, PN_PCI_INTLINE);			/* Reset the power state. */			printf("pn%d: chip is in D%d power mode "			"-- setting to D0\n", unit, command & PN_PSTATE_MASK);			command &= 0xFFFFFFFC;			pci_conf_write(config_id, PN_PCI_PWRMGMTCTRL, command);			/* Restore PCI config data. */			pci_conf_write(config_id, PN_PCI_LOIO, iobase);			pci_conf_write(config_id, PN_PCI_LOMEM, membase);			pci_conf_write(config_id, PN_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 PN_USEIOSPACE	if (!(command & PCIM_CMD_PORTEN)) {		printf("pn%d: failed to enable I/O ports!\n", unit);		free(sc, M_DEVBUF);		goto fail;	}	if (!pci_map_port(config_id, PN_PCI_LOIO,					(u_short *)&(sc->pn_bhandle))) {		printf ("pn%d: couldn't map ports\n", unit);		goto fail;	}#ifdef __i386__	sc->pn_btag = I386_BUS_SPACE_IO;#endif#ifdef __alpha__	sc->pn_btag = ALPHA_BUS_SPACE_IO;#endif#else	if (!(command & PCIM_CMD_MEMEN)) {		printf("pn%d: failed to enable memory mapping!\n", unit);		goto fail;	}	if (!pci_map_mem(config_id, PN_PCI_LOMEM, &vbase, &pbase)) {		printf ("pn%d: couldn't map memory\n", unit);		goto fail;	}	sc->pn_bhandle = vbase;#ifdef __i386__	sc->pn_btag = I386_BUS_SPACE_MEM;#endif#ifdef __alpha__	sc->pn_btag = ALPHA_BUS_SPACE_MEM;#endif#endif	/* Allocate interrupt */	if (!pci_map_int(config_id, pn_intr, sc, &net_imask)) {		printf("pn%d: couldn't map interrupt\n", unit);		goto fail;	}	/* Save the cache line size. */	sc->pn_cachesize = pci_conf_read(config_id, PN_PCI_CACHELEN) & 0xFF;	/* Reset the adapter. */	pn_reset(sc);	/*	 * Get station address from the EEPROM.	 */	pn_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1);	/*	 * A PNIC chip was detected. Inform the world.	 */	printf("pn%d: Ethernet address: %6D\n", unit, eaddr, ":");	sc->pn_unit = unit;	bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);	sc->pn_ldata_ptr = malloc(sizeof(struct pn_list_data) + 8,				M_DEVBUF, M_NOWAIT);	if (sc->pn_ldata_ptr == NULL) {		free(sc, M_DEVBUF);		printf("pn%d: no memory for list buffers!\n", unit);		goto fail;	}	sc->pn_ldata = (struct pn_list_data *)sc->pn_ldata_ptr;	round = (unsigned int)sc->pn_ldata_ptr & 0xF;	roundptr = sc->pn_ldata_ptr;	for (i = 0; i < 8; i++) {		if (round % 8) {			round++;			roundptr++;		} else			break;	}	sc->pn_ldata = (struct pn_list_data *)roundptr;	bzero(sc->pn_ldata, sizeof(struct pn_list_data));#ifdef PN_RX_BUG_WAR	revision = pci_conf_read(config_id, PN_PCI_REVISION) & 0x000000FF;	if (revision == PN_169B_REV || revision == PN_169_REV ||	    (revision & 0xF0) == PN_168_REV) {		sc->pn_rx_war = 1;		sc->pn_rx_buf = malloc(PN_RXLEN * 5, M_DEVBUF, M_NOWAIT);		if (sc->pn_rx_buf == NULL) {			printf("pn%d: no memory for workaround buffer\n", unit);			goto fail;		}	} else {		sc->pn_rx_war = 0;	}#endif	ifp = &sc->arpcom.ac_if;	ifp->if_softc = sc;	ifp->if_unit = unit;	ifp->if_name = "pn";	ifp->if_mtu = ETHERMTU;	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;

⌨️ 快捷键说明

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