if_mx.c

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

C
2,495
字号
	pcidi_t			device_id;{	struct mx_type		*t;	u_int32_t		rev;	t = mx_devs;	while(t->mx_name != NULL) {		if ((device_id & 0xFFFF) == t->mx_vid &&		    ((device_id >> 16) & 0xFFFF) == t->mx_did) {			/* Check the PCI revision */			rev = pci_conf_read(config_id, MX_PCI_REVID) & 0xFF;			if (t->mx_did == MX_DEVICEID_98713 &&						rev >= MX_REVISION_98713A)				t++;			if (t->mx_did == CP_DEVICEID_98713 &&						rev >= MX_REVISION_98713A)				t++;			if (t->mx_did == MX_DEVICEID_987x5 &&						rev >= MX_REVISION_98725)				t++;			return(t->mx_name);		}		t++;	}	return(NULL);}/* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */static voidmx_attach(config_id, unit)	pcici_t			config_id;	int			unit;{	int			s, i;#ifndef MX_USEIOSPACE	vm_offset_t		pbase, vbase;#endif	u_char			eaddr[ETHER_ADDR_LEN];	u_int32_t		command;	struct mx_softc		*sc;	struct ifnet		*ifp;	int			media = IFM_ETHER|IFM_100_TX|IFM_FDX;	unsigned int		round;	caddr_t			roundptr;	struct mx_type		*p;	u_int16_t		phy_vid, phy_did, phy_sts, mac_offset = 0;	u_int32_t		revision, pci_id;	s = splimp();	sc = malloc(sizeof(struct mx_softc), M_DEVBUF, M_NOWAIT);	if (sc == NULL) {		printf("mx%d: no memory for softc struct!\n", unit);		goto fail;	}	bzero(sc, sizeof(struct mx_softc));	/*	 * Handle power management nonsense.	 */	command = pci_conf_read(config_id, MX_PCI_CAPID) & 0x000000FF;	if (command == 0x01) {		command = pci_conf_read(config_id, MX_PCI_PWRMGMTCTRL);		if (command & MX_PSTATE_MASK) {			u_int32_t		iobase, membase, irq;			/* Save important PCI config data. */			iobase = pci_conf_read(config_id, MX_PCI_LOIO);			membase = pci_conf_read(config_id, MX_PCI_LOMEM);			irq = pci_conf_read(config_id, MX_PCI_INTLINE);			/* Reset the power state. */			printf("mx%d: chip is in D%d power mode "			"-- setting to D0\n", unit, command & MX_PSTATE_MASK);			command &= 0xFFFFFFFC;			pci_conf_write(config_id, MX_PCI_PWRMGMTCTRL, command);			/* Restore PCI config data. */			pci_conf_write(config_id, MX_PCI_LOIO, iobase);			pci_conf_write(config_id, MX_PCI_LOMEM, membase);			pci_conf_write(config_id, MX_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 MX_USEIOSPACE	if (!(command & PCIM_CMD_PORTEN)) {		printf("mx%d: failed to enable I/O ports!\n", unit);		free(sc, M_DEVBUF);		goto fail;	}	if (!pci_map_port(config_id, MX_PCI_LOIO,					(u_short *)&(sc->mx_bhandle))) {		printf ("mx%d: couldn't map ports\n", unit);		goto fail;        }#ifdef __i386__	sc->mx_btag = I386_BUS_SPACE_IO;#endif#ifdef __alpha__	sc->mx_btag = ALPHA_BUS_SPACE_IO;#endif#else	if (!(command & PCIM_CMD_MEMEN)) {		printf("mx%d: failed to enable memory mapping!\n", unit);		goto fail;	}	if (!pci_map_mem(config_id, MX_PCI_LOMEM, &vbase, &pbase)) {		printf ("mx%d: couldn't map memory\n", unit);		goto fail;	}#ifdef __i386__	sc->mx_btag = I386_BUS_SPACE_MEM;#endif#ifdef __alpha__	sc->mx_btag = ALPHA_BUS_SPACE_MEM;#endif	sc->mx_bhandle = vbase;#endif	/* Allocate interrupt */	if (!pci_map_int(config_id, mx_intr, sc, &net_imask)) {		printf("mx%d: couldn't map interrupt\n", unit);		goto fail;	}	/* Need this info to decide on a chip type. */	revision = pci_conf_read(config_id, MX_PCI_REVID) & 0x000000FF;	pci_id = (pci_conf_read(config_id,MX_PCI_VENDOR_ID) >> 16) & 0x0000FFFF;	if (pci_id == MX_DEVICEID_98713 && revision < MX_REVISION_98713A)		sc->mx_type = MX_TYPE_98713;	else if (pci_id == CP_DEVICEID_98713 && revision < MX_REVISION_98713A)		sc->mx_type = MX_TYPE_98713;	else if (pci_id == MX_DEVICEID_98713 && revision >= MX_REVISION_98713A)		sc->mx_type = MX_TYPE_98713A;	else		sc->mx_type = MX_TYPE_987x5;	/* Save the cache line size. */	sc->mx_cachesize = pci_conf_read(config_id, MX_PCI_CACHELEN) & 0xFF;	/* Reset the adapter. */	mx_reset(sc);	/*	 * Get station address from the EEPROM.	 */	mx_read_eeprom(sc, (caddr_t)&mac_offset,			(MX_EE_NODEADDR_OFFSET / 2), 1, 0);	mx_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0);	/*	 * A PMAC chip was detected. Inform the world.	 */	printf("mx%d: Ethernet address: %6D\n", unit, eaddr, ":");	sc->mx_unit = unit;	bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);	sc->mx_ldata_ptr = malloc(sizeof(struct mx_list_data) + 8,				M_DEVBUF, M_NOWAIT);	if (sc->mx_ldata_ptr == NULL) {		free(sc, M_DEVBUF);		printf("mx%d: no memory for list buffers!\n", unit);		goto fail;	}	sc->mx_ldata = (struct mx_list_data *)sc->mx_ldata_ptr;	round = (unsigned int)sc->mx_ldata_ptr & 0xF;	roundptr = sc->mx_ldata_ptr;	for (i = 0; i < 8; i++) {		if (round % 8) {			round++;			roundptr++;		}			break;	}	sc->mx_ldata = (struct mx_list_data *)roundptr;	bzero(sc->mx_ldata, sizeof(struct mx_list_data));	ifp = &sc->arpcom.ac_if;	ifp->if_softc = sc;	ifp->if_unit = unit;	ifp->if_name = "mx";	ifp->if_mtu = ETHERMTU;	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;	ifp->if_ioctl = mx_ioctl;	ifp->if_output = ether_output;	ifp->if_start = mx_start;	ifp->if_watchdog = mx_watchdog;	ifp->if_init = mx_init;	ifp->if_baudrate = 10000000;	ifp->if_snd.ifq_maxlen = MX_TX_LIST_CNT - 1;	if (sc->mx_type == MX_TYPE_98713) {		if (bootverbose)			printf("mx%d: probing for a PHY\n", sc->mx_unit);		for (i = MX_PHYADDR_MIN; i < MX_PHYADDR_MAX + 1; i++) {			if (bootverbose)				printf("mx%d: checking address: %d\n",							sc->mx_unit, i);			sc->mx_phy_addr = i;			mx_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);			DELAY(500);			while(mx_phy_readreg(sc, PHY_BMCR)					& PHY_BMCR_RESET);			if ((phy_sts = mx_phy_readreg(sc, PHY_BMSR)))				break;		}		if (phy_sts) {			phy_vid = mx_phy_readreg(sc, PHY_VENID);			phy_did = mx_phy_readreg(sc, PHY_DEVID);			if (bootverbose)				printf("mx%d: found PHY at address %d, ",					sc->mx_unit, sc->mx_phy_addr);			if (bootverbose)				printf("vendor id: %x device id: %x\n",				phy_vid, phy_did);			p = mx_phys;			while(p->mx_vid) {				if (phy_vid == p->mx_vid &&					(phy_did | 0x000F) == p->mx_did) {					sc->mx_pinfo = p;					break;				}				p++;			}			if (sc->mx_pinfo == NULL)				sc->mx_pinfo = &mx_phys[PHY_UNKNOWN];			if (bootverbose)				printf("mx%d: PHY type: %s\n",					sc->mx_unit, sc->mx_pinfo->mx_name);		} else {#ifdef DIAGNOSTIC			printf("mx%d: MII without any phy!\n", sc->mx_unit);#endif		}	}	/*	 * Do ifmedia setup.	 */	ifmedia_init(&sc->ifmedia, 0, mx_ifmedia_upd, mx_ifmedia_sts);	if (sc->mx_type == MX_TYPE_98713 && sc->mx_pinfo != NULL) {		mx_getmode_mii(sc);		mx_autoneg_mii(sc, MX_FLAG_FORCEDELAY, 1);	} else {		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_10_T, 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_TX, 0, NULL);		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);		mx_autoneg(sc, MX_FLAG_FORCEDELAY, 1);	}	media = sc->ifmedia.ifm_media;	mx_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(mx_shutdown, sc, SHUTDOWN_POST_SYNC);fail:	splx(s);	return;}/* * Initialize the transmit descriptors. */static int mx_list_tx_init(sc)	struct mx_softc		*sc;{	struct mx_chain_data	*cd;	struct mx_list_data	*ld;	int			i;	cd = &sc->mx_cdata;	ld = sc->mx_ldata;	for (i = 0; i < MX_TX_LIST_CNT; i++) {		cd->mx_tx_chain[i].mx_ptr = &ld->mx_tx_list[i];		if (i == (MX_TX_LIST_CNT - 1))			cd->mx_tx_chain[i].mx_nextdesc =				&cd->mx_tx_chain[0];		else			cd->mx_tx_chain[i].mx_nextdesc =				&cd->mx_tx_chain[i + 1];	}	cd->mx_tx_free = &cd->mx_tx_chain[0];	cd->mx_tx_tail = cd->mx_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 mx_list_rx_init(sc)	struct mx_softc		*sc;{	struct mx_chain_data	*cd;	struct mx_list_data	*ld;	int			i;	cd = &sc->mx_cdata;	ld = sc->mx_ldata;	for (i = 0; i < MX_RX_LIST_CNT; i++) {		cd->mx_rx_chain[i].mx_ptr =			(struct mx_desc *)&ld->mx_rx_list[i];		if (mx_newbuf(sc, &cd->mx_rx_chain[i]) == ENOBUFS)			return(ENOBUFS);		if (i == (MX_RX_LIST_CNT - 1)) {			cd->mx_rx_chain[i].mx_nextdesc = &cd->mx_rx_chain[0];			ld->mx_rx_list[i].mx_next =					vtophys(&ld->mx_rx_list[0]);		} else {			cd->mx_rx_chain[i].mx_nextdesc = &cd->mx_rx_chain[i + 1];			ld->mx_rx_list[i].mx_next =					vtophys(&ld->mx_rx_list[i + 1]);		}	}	cd->mx_rx_head = &cd->mx_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 mx_newbuf(sc, c)	struct mx_softc		*sc;	struct mx_chain_onefrag	*c;{	struct mbuf		*m_new = NULL;	MGETHDR(m_new, M_DONTWAIT, MT_DATA);	if (m_new == NULL) {		printf("mx%d: no memory for rx list -- packet dropped!\n",								sc->mx_unit);		return(ENOBUFS);	}	MCLGET(m_new, M_DONTWAIT);	if (!(m_new->m_flags & M_EXT)) {		printf("mx%d: no memory for rx list -- packet dropped!\n",								sc->mx_unit);		m_freem(m_new);		return(ENOBUFS);	}	c->mx_mbuf = m_new;	c->mx_ptr->mx_status = MX_RXSTAT;	c->mx_ptr->mx_data = vtophys(mtod(m_new, caddr_t));	c->mx_ptr->mx_ctl = MX_RXCTL_RLINK | (MCLBYTES - 1);	return(0);}/* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */static void mx_rxeof(sc)	struct mx_softc		*sc;{        struct ether_header	*eh;        struct mbuf		*m;        struct ifnet		*ifp;	struct mx_chain_onefrag	*cur_rx;	int			total_len = 0;	u_int32_t		rxstat;	ifp = &sc->arpcom.ac_if;	while(!((rxstat = sc->mx_cdata.mx_rx_head->mx_ptr->mx_status) &							MX_RXSTAT_OWN)) {#ifdef __alpha__		struct mbuf		*m0 = NULL;#endif		cur_rx = sc->mx_cdata.mx_rx_head;		sc->mx_cdata.mx_rx_head = cur_rx->mx_nextdesc;		/*		 * 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 & MX_RXSTAT_RXERR) {			ifp->if_ierrors++;			if (rxstat & MX_RXSTAT_COLLSEEN)				ifp->if_collisions++;			cur_rx->mx_ptr->mx_status = MX_RXSTAT;			cur_rx->mx_ptr->mx_ctl =				MX_RXCTL_RLINK | (MCLBYTES - 1);			continue;		}		/* No errors; receive the packet. */			m = cur_rx->mx_mbuf;		total_len = MX_RXBYTES(cur_rx->mx_ptr->mx_status);		/*		 * XXX The Macronix chips includes the CRC with every		 * received frame, and there's no way to turn this		 * behavior off (at least, I can't find anything in	 	 * the manual that explains how to do it) so we have		 * to trim off the CRC manually.		 */		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 (mx_newbuf(sc, cur_rx) == ENOBUFS) {			ifp->if_ierrors++;			cur_rx->mx_ptr->mx_status = MX_RXSTAT;			cur_rx->mx_ptr->mx_ctl =					MX_RXCTL_RLINK | (MCLBYTES - 1);			continue;		}#ifdef __alpha__		/*		 * Deal with alignment on alpha.		 */		MGETHDR(m0, M_DONTWAIT, MT_DATA);		if (m0 == NULL) {			ifp->if_ierrors++;			cur_rx->mx_ptr->mx_status = MX_RXSTAT;			cur_rx->mx_ptr->mx_ctl =					MX_RXCTL_RLINK | (MCLBYTES - 1);			bzero((char *)mtod(cur_rx->mx_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 mx_rxeoc(sc)	struct mx_softc		*sc;{	mx_rxeof(sc);	MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_RX_ON);	CSR_WRITE_4(sc, MX_RXADDR, vtophys(sc->mx_cdata.mx_rx_head->mx_ptr));	MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_ON);	CSR_WRITE_4(sc, MX_RXSTART, 0xFFFFFFFF);	return;}/* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */static void mx_txeof(sc)	struct mx_softc		*sc;{	struct mx_chain		*cur_tx;	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	/* Clear the timeout timer. */	ifp->if_timer = 0;	if (sc->mx_cdata.mx_tx_head == NULL)		return;	/*	 * Go through our tx list and free mbufs for those	 * frames that have been transmitted.	 */	while(sc->mx_cdata.mx_tx_head->mx_mbuf != NULL) {		u_int32_t		txstat;		cur_tx = sc->mx_cdata.mx_tx_head;		txstat = MX_TXSTATUS(cur_tx);		if (txstat & MX_TXSTAT_OWN)			break;		if (txstat & MX_TXSTAT_ERRSUM) {			ifp->if_oerrors++;			if (txstat & MX_TXSTAT_EXCESSCOLL)				ifp->if_collisions++;			if (txstat & MX_TXSTAT_LATECOLL)				ifp->if_collisions++;		}		ifp->if_collisions += (txstat & MX_TXSTAT_COLLCNT) >> 3;		ifp->if_opackets++;		m_freem(cur_tx->mx_mbuf);		cur_tx->mx_mbuf = NULL;		if (sc->mx_cdata.mx_tx_head == sc->mx_cdata.mx_tx_tail) {			sc->mx_cdata.mx_tx_head = NULL;			sc->mx_cdata.mx_tx_tail = NULL;			break;		}		sc->mx_cdata.mx_tx_head = cur_tx->mx_nextdesc;	}	return;}/* * TX 'end of channel' interrupt handler. */static void mx_txeoc(sc)	struct mx_softc		*sc;{	struct ifnet		*ifp;	ifp = &sc->arpcom.ac_if;	ifp->if_timer = 0;	if (sc->mx_cdata.mx_tx_head == NULL) {		ifp->if_flags &= ~IFF_OACTIVE;		sc->mx_cdata.mx_tx_tail = NULL;		if (sc->mx_want_auto) {			if (sc->mx_type == MX_TYPE_98713 &&						sc->mx_pinfo != NULL)				mx_autoneg_mii(sc, MX_FLAG_DELAYTIMEO, 1);			else				mx_autoneg(sc, MX_FLAG_DELAYTIMEO, 1);		}	}

⌨️ 快捷键说明

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