if_pn.c

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

C
2,280
字号
	ifp->if_ioctl = pn_ioctl;	ifp->if_output = ether_output;	ifp->if_start = pn_start;	ifp->if_watchdog = pn_watchdog;	ifp->if_init = pn_init;	ifp->if_baudrate = 10000000;	ifp->if_snd.ifq_maxlen = PN_TX_LIST_CNT - 1;	ifmedia_init(&sc->ifmedia, 0, pn_ifmedia_upd, pn_ifmedia_sts);	if (bootverbose)		printf("pn%d: probing for a PHY\n", sc->pn_unit);	for (i = PN_PHYADDR_MIN; i < PN_PHYADDR_MAX + 1; i++) {		if (bootverbose)			printf("pn%d: checking address: %d\n",						sc->pn_unit, i);		sc->pn_phy_addr = i;		pn_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);		DELAY(500);		while(pn_phy_readreg(sc, PHY_BMCR)				& PHY_BMCR_RESET);		if ((phy_sts = pn_phy_readreg(sc, PHY_BMSR)))			break;	}	if (phy_sts) {		phy_vid = pn_phy_readreg(sc, PHY_VENID);		phy_did = pn_phy_readreg(sc, PHY_DEVID);		if (bootverbose)			printf("pn%d: found PHY at address %d, ",					sc->pn_unit, sc->pn_phy_addr);		if (bootverbose)			printf("vendor id: %x device id: %x\n",				phy_vid, phy_did);		p = pn_phys;		while(p->pn_vid) {			if (phy_vid == p->pn_vid &&				(phy_did | 0x000F) == p->pn_did) {				sc->pn_pinfo = p;				break;			}			p++;		}		if (sc->pn_pinfo == NULL)			sc->pn_pinfo = &pn_phys[PHY_UNKNOWN];		if (bootverbose)			printf("pn%d: PHY type: %s\n",				sc->pn_unit, sc->pn_pinfo->pn_name);		pn_getmode_mii(sc);		pn_autoneg_mii(sc, PN_FLAG_FORCEDELAY, 1);	} else {		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);		ifmedia_add(&sc->ifmedia,		    IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL);		ifmedia_add(&sc->ifmedia,		    IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);		pn_autoneg(sc, PN_FLAG_FORCEDELAY, 1);	}	media = sc->ifmedia.ifm_media;	pn_stop(sc);	ifmedia_set(&sc->ifmedia, media);	/*	 * Call MI attach routines.	 */	if_attach(ifp);	ether_ifattach(ifp);#if NBPFILTER > 0	bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));#endif	at_shutdown(pn_shutdown, sc, SHUTDOWN_POST_SYNC);fail:	splx(s);	return;}/* * Initialize the transmit descriptors. */static int pn_list_tx_init(sc)	struct pn_softc		*sc;{	struct pn_chain_data	*cd;	struct pn_list_data	*ld;	int			i;	cd = &sc->pn_cdata;	ld = sc->pn_ldata;	for (i = 0; i < PN_TX_LIST_CNT; i++) {		cd->pn_tx_chain[i].pn_ptr = &ld->pn_tx_list[i];		if (i == (PN_TX_LIST_CNT - 1))			cd->pn_tx_chain[i].pn_nextdesc =				&cd->pn_tx_chain[0];		else			cd->pn_tx_chain[i].pn_nextdesc =				&cd->pn_tx_chain[i + 1];	}	cd->pn_tx_free = &cd->pn_tx_chain[0];	cd->pn_tx_tail = cd->pn_tx_head = NULL;	return(0);}/* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */static int pn_list_rx_init(sc)	struct pn_softc		*sc;{	struct pn_chain_data	*cd;	struct pn_list_data	*ld;	int			i;	cd = &sc->pn_cdata;	ld = sc->pn_ldata;	for (i = 0; i < PN_RX_LIST_CNT; i++) {		cd->pn_rx_chain[i].pn_ptr =			(struct pn_desc *)&ld->pn_rx_list[i];		if (pn_newbuf(sc, &cd->pn_rx_chain[i]) == ENOBUFS)			return(ENOBUFS);		if (i == (PN_RX_LIST_CNT - 1)) {			cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[0];			ld->pn_rx_list[i].pn_next =					vtophys(&ld->pn_rx_list[0]);		} else {			cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[i + 1];			ld->pn_rx_list[i].pn_next =					vtophys(&ld->pn_rx_list[i + 1]);		}	}	cd->pn_rx_head = &cd->pn_rx_chain[0];	return(0);}/* * Initialize an RX descriptor and attach an MBUF cluster. * Note: the length fields are only 11 bits wide, which means the * largest size we can specify is 2047. This is important because * MCLBYTES is 2048, so we have to subtract one otherwise we'll * overflow the field and make a mess. */static int pn_newbuf(sc, c)	struct pn_softc		*sc;	struct pn_chain_onefrag	*c;{	struct mbuf		*m_new = NULL;	MGETHDR(m_new, M_DONTWAIT, MT_DATA);	if (m_new == NULL) {		printf("pn%d: no memory for rx list -- packet dropped!\n",								sc->pn_unit);		return(ENOBUFS);	}	MCLGET(m_new, M_DONTWAIT);	if (!(m_new->m_flags & M_EXT)) {		printf("pn%d: no memory for rx list -- packet dropped!\n",								sc->pn_unit);		m_freem(m_new);		return(ENOBUFS);	}	/*	 * Zero the buffer. This is part of the workaround for the	 * promiscuous mode bug in the revision 33 PNIC chips.	 */	bzero((char *)mtod(m_new, char *), MCLBYTES);	m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;	c->pn_mbuf = m_new;	c->pn_ptr->pn_status = PN_RXSTAT;	c->pn_ptr->pn_data = vtophys(mtod(m_new, caddr_t));	c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN;	return(0);}#ifdef PN_RX_BUG_WAR/* * Grrrrr. * The PNIC chip has a terrible bug in it that manifests itself during * periods of heavy activity. The exact mode of failure if difficult to * pinpoint: sometimes it only happens in promiscuous mode, sometimes it * will happen on slow machines. The bug is that sometimes instead of * uploading one complete frame during reception, it uploads what looks * like the entire contents of its FIFO memory. The frame we want is at * the end of the whole mess, but we never know exactly how much data has * been uploaded, so salvaging the frame is hard. * * There is only one way to do it reliably, and it's disgusting. * Here's what we know: * * - We know there will always be somewhere between one and three extra *   descriptors uploaded. * * - We know the desired received frame will always be at the end of the *   total data upload. * * - We know the size of the desired received frame because it will be *   provided in the length field of the status word in the last descriptor. * * Here's what we do: * * - When we allocate buffers for the receive ring, we bzero() them. *   This means that we know that the buffer contents should be all *   zeros, except for data uploaded by the chip. * * - We also force the PNIC chip to upload frames that include the *   ethernet CRC at the end. * * - We gather all of the bogus frame data into a single buffer. * * - We then position a pointer at the end of this buffer and scan *   backwards until we encounter the first non-zero byte of data. *   This is the end of the received frame. We know we will encounter *   some data at the end of the frame because the CRC will always be *   there, so even if the sender transmits a packet of all zeros, *   we won't be fooled. * * - We know the size of the actual received frame, so we subtract *   that value from the current pointer location. This brings us *   to the start of the actual received packet. * * - We copy this into an mbuf and pass it on, along with the actual *   frame length. * * The performance hit is tremendous, but it beats dropping frames all * the time. */#define PN_WHOLEFRAME	(PN_RXSTAT_FIRSTFRAG|PN_RXSTAT_LASTFRAG)static void pn_rx_bug_war(sc, cur_rx)	struct pn_softc		*sc;	struct pn_chain_onefrag	*cur_rx;{	struct pn_chain_onefrag	*c;	unsigned char		*ptr;	int			total_len;	u_int32_t		rxstat = 0;	c = sc->pn_rx_bug_save;	ptr = sc->pn_rx_buf;	bzero(ptr, sizeof(PN_RXLEN * 5));	/* Copy all the bytes from the bogus buffers. */	while ((c->pn_ptr->pn_status & PN_WHOLEFRAME) != PN_WHOLEFRAME) {		rxstat = c->pn_ptr->pn_status;		m_copydata(c->pn_mbuf, 0, PN_RXLEN, ptr);		ptr += PN_RXLEN - 2; /* round down to 32-bit boundary */		if (c == cur_rx)			break;		if (rxstat & PN_RXSTAT_LASTFRAG)			break;		c->pn_ptr->pn_status = PN_RXSTAT;		c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN;		bzero((char *)mtod(c->pn_mbuf, char *), MCLBYTES);		c = c->pn_nextdesc;	}	/* Find the length of the actual receive frame. */	total_len = PN_RXBYTES(rxstat);	/* Scan backwards until we hit a non-zero byte. */	while(*ptr == 0x00)		ptr--;	/* Round off. */	if ((u_int32_t)(ptr) & 0x3)		ptr -= 1;	/* Now find the start of the frame. */	ptr -= total_len;	if (ptr < sc->pn_rx_buf)		ptr = sc->pn_rx_buf;	/*	 * Now copy the salvaged frame to the last mbuf and fake up	 * the status word to make it look like a successful 	 * frame reception.	 */	m_copyback(cur_rx->pn_mbuf, 0, total_len, ptr);	cur_rx->pn_mbuf->m_len = c->pn_mbuf->m_pkthdr.len = MCLBYTES;	cur_rx->pn_ptr->pn_status |= PN_RXSTAT_FIRSTFRAG;	return;}#endif/* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */static void pn_rxeof(sc)	struct pn_softc		*sc;{        struct ether_header	*eh;        struct mbuf		*m;        struct ifnet		*ifp;	struct pn_chain_onefrag	*cur_rx;	int			total_len = 0;	u_int32_t		rxstat;	ifp = &sc->arpcom.ac_if;	while(!((rxstat = sc->pn_cdata.pn_rx_head->pn_ptr->pn_status) &							PN_RXSTAT_OWN)) {#ifdef __alpha__		struct mbuf		*m0 = NULL;#endif		cur_rx = sc->pn_cdata.pn_rx_head;		sc->pn_cdata.pn_rx_head = cur_rx->pn_nextdesc;#ifdef PN_RX_BUG_WAR		/*		 * XXX The PNIC has a nasty receiver bug that manifests	 	 * under certain conditions (sometimes only in promiscuous		 * mode, sometimes only on slow machines even when not in		 * promiscuous mode). We have to keep an eye out for the		 * failure condition and employ a workaround to recover		 * any mangled frames.		 */		if (sc->pn_rx_war) {			if ((rxstat & PN_WHOLEFRAME) != PN_WHOLEFRAME) {				if (rxstat & PN_RXSTAT_FIRSTFRAG)					sc->pn_rx_bug_save = cur_rx;				if ((rxstat & PN_RXSTAT_LASTFRAG) == 0)					continue;				pn_rx_bug_war(sc, cur_rx);				rxstat = cur_rx->pn_ptr->pn_status;			}		}#endif		/*		 * If an error occurs, update stats, clear the		 * status word and leave the mbuf cluster in place:		 * it should simply get re-used next time this descriptor	 	 * comes up in the ring.		 */		if (rxstat & PN_RXSTAT_RXERR) {			ifp->if_ierrors++;			if (rxstat & PN_RXSTAT_COLLSEEN)				ifp->if_collisions++;			cur_rx->pn_ptr->pn_status = PN_RXSTAT;			cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN;			bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES);			continue;		}		/* No errors; receive the packet. */			m = cur_rx->pn_mbuf;		total_len = PN_RXBYTES(cur_rx->pn_ptr->pn_status);		/* Trim off the CRC. */		total_len -= ETHER_CRC_LEN;		/*		 * Try to conjure up a new mbuf cluster. If that		 * fails, it means we have an out of memory condition and		 * should leave the buffer in place and continue. This will		 * result in a lost packet, but there's little else we		 * can do in this situation.		 */		if (pn_newbuf(sc, cur_rx) == ENOBUFS) {			ifp->if_ierrors++;			cur_rx->pn_ptr->pn_status = PN_RXSTAT;			cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN;			bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES);			continue;		}#ifdef __alpha__		/*		 * Grrrr! On the alpha platform, the start of the		 * packet data must be longword aligned so that ip_input()		 * doesn't perform any unaligned accesses when it tries		 * to fiddle with the IP header. But the PNIC is stupid		 * and wants RX buffers to start on longword boundaries.		 * So we can't just shift the DMA address over a few		 * bytes to alter the payload alignment. Instead, we		 * have to chop out ethernet and IP header parts of		 * the packet and place then in a separate mbuf with		 * the alignment fixed up properly.		 *		 * As if this chip wasn't broken enough already.		 */		MGETHDR(m0, M_DONTWAIT, MT_DATA);		if (m0 == NULL) {			ifp->if_ierrors++;			cur_rx->pn_ptr->pn_status = PN_RXSTAT;			cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN;			bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES);			continue;		}		m0->m_data += 2;		if (total_len <= (MHLEN - 2)) {			bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), total_len);			m_freem(m);			m = m0;			m->m_pkthdr.len = m->m_len = total_len;		} else {			bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), (MHLEN - 2));			m->m_len = total_len - (MHLEN - 2);			m->m_data += (MHLEN - 2);			m0->m_next = m;			m0->m_len = (MHLEN - 2);			m = m0;			m->m_pkthdr.len = total_len;		}#else		m->m_pkthdr.len = m->m_len = total_len;#endif		ifp->if_ipackets++;		eh = mtod(m, struct ether_header *);		m->m_pkthdr.rcvif = ifp;#if NBPFILTER > 0		/*		 * Handle BPF listeners. Let the BPF user see the packet, but		 * don't pass it up to the ether_input() layer unless it's		 * a broadcast packet, multicast packet, matches our ethernet		 * address or the interface is in promiscuous mode.		 */		if (ifp->if_bpf) {			bpf_mtap(ifp, m);			if (ifp->if_flags & IFF_PROMISC &&				(bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,						ETHER_ADDR_LEN) &&					(eh->ether_dhost[0] & 1) == 0)) {				m_freem(m);				continue;			}		}#endif		/* Remove header from mbuf and pass it on. */		m_adj(m, sizeof(struct ether_header));		ether_input(ifp, eh, m);	}	return;}void pn_rxeoc(sc)	struct pn_softc		*sc;{	pn_rxeof(sc);	PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON);	CSR_WRITE_4(sc, PN_RXADDR, vtophys(sc->pn_cdata.pn_rx_head->pn_ptr));	PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON);	CSR_WRITE_4(sc, PN_RXSTART, 0xFFFFFFFF);	return;}/* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */static void pn_txeof(sc)	struct pn_softc		*sc;{	struct pn_chain		*cur_tx;	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	/* Clear the timeout timer. */	ifp->if_timer = 0;	if (sc->pn_cdata.pn_tx_head == NULL)		return;	/*	 * Go through our tx list and free mbufs for those	 * frames that have been transmitted.	 */	while(sc->pn_cdata.pn_tx_head->pn_mbuf != NULL) {		u_int32_t		txstat;		cur_tx = sc->pn_cdata.pn_tx_head;		txstat = PN_TXSTATUS(cur_tx);		if (txstat & PN_TXSTAT_OWN)			break;		if (txstat & PN_TXSTAT_ERRSUM) {			ifp->if_oerrors++;			if (txstat & PN_TXSTAT_EXCESSCOLL)				ifp->if_collisions++;			if (txstat & PN_TXSTAT_LATECOLL)				ifp->if_collisions++;		}		ifp->if_collisions += (txstat & PN_TXSTAT_COLLCNT) >> 3;		ifp->if_opackets++;		m_freem(cur_tx->pn_mbuf);		cur_tx->pn_mbuf = NULL;		if (sc->pn_cdata.pn_tx_head == sc->pn_cdata.pn_tx_tail) {			sc->pn_cdata.pn_tx_head = NULL;			sc->pn_cdata.pn_tx_tail = NULL;			break;		}		sc->pn_cdata.pn_tx_head = cur_tx->pn_nextdesc;	}	return;}/* * TX 'end of channel' interrupt handler. */static void pn_txeoc(sc)	struct pn_softc		*sc;{	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	ifp->if_timer = 0;	if (sc->pn_cdata.pn_tx_head == NULL) {		ifp->if_flags &= ~IFF_OACTIVE;		sc->pn_cdata.pn_tx_tail = NULL;		if (sc->pn_want_auto) {			if (sc->pn_pinfo == NULL)				pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1);			else				pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1);		}	}	return;}static void pn_intr(arg)	void			*arg;{	struct pn_softc		*sc;	struct ifnet		*ifp;	u_int32_t		status;	sc = arg;	ifp = &sc->arpcom.ac_if;	/* Supress unwanted interrupts. */	if (!(ifp->if_flags & IFF_UP)) {		pn_stop(sc);

⌨️ 快捷键说明

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