⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fec.c

📁 实现一个网卡到内存空间的零拷贝程序
💻 C
📖 第 1 页 / 共 4 页
字号:
 * Skeleton taken from sunlance driver. * The CPM Ethernet implementation allows Multicast as well as individual * MAC address filtering.  Some of the drivers check to make sure it is * a group multicast address, and discard those that are not.  I guess I * will do the same for now, but just remove the test if you want * individual filtering as well (do the upper net layers want or support * this kind of feature?). */static void set_multicast_list(struct net_device *dev){	struct	fec_enet_private *fep;	volatile fec_t *ep;	fep = (struct fec_enet_private *)dev->priv;	ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec);	if (dev->flags&IFF_PROMISC) {		/* Log any net taps. */		printk("%s: Promiscuous mode enabled.\n", dev->name);		ep->fec_r_cntrl |= FEC_RCNTRL_PROM;	} else {		ep->fec_r_cntrl &= ~FEC_RCNTRL_PROM;		if (dev->flags & IFF_ALLMULTI) {			/* Catch all multicast addresses, so set the			 * filter to all 1's.			 */			ep->fec_hash_table_high = 0xffffffff;			ep->fec_hash_table_low = 0xffffffff;		}#if 0		else {			/* Clear filter and add the addresses in the list.			*/			ep->sen_gaddr1 = 0;			ep->sen_gaddr2 = 0;			ep->sen_gaddr3 = 0;			ep->sen_gaddr4 = 0;			dmi = dev->mc_list;			for (i=0; i<dev->mc_count; i++) {				/* Only support group multicast for now.				*/				if (!(dmi->dmi_addr[0] & 1))					continue;				/* The address in dmi_addr is LSB first,				 * and taddr is MSB first.  We have to				 * copy bytes MSB first from dmi_addr.				 */				mcptr = (u_char *)dmi->dmi_addr + 5;				tdptr = (u_char *)&ep->sen_taddrh;				for (j=0; j<6; j++)					*tdptr++ = *mcptr--;				/* Ask CPM to run CRC and set bit in				 * filter mask.				 */				cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG;				/* this delay is necessary here -- Cort */				udelay(10);				while (cpmp->cp_cpcr & CPM_CR_FLG);			}		}#endif	}}/* Initialize the FEC Ethernet on 860T. */int __init fec_enet_init(void){	struct net_device *dev;	struct fec_enet_private *fep;	int i, j;	unsigned char	*eap, *iap;	unsigned long	mem_addr;	pte_t		*pte;	volatile	cbd_t	*bdp;	cbd_t		*cbd_base;	volatile	immap_t	*immap;	volatile	fec_t	*fecp;	bd_t		*bd;	extern		uint	_get_IMMR(void);#ifdef CONFIG_SCC_ENET	unsigned char	tmpaddr[6];#endif	immap = (immap_t *)IMAP_ADDR;	/* pointer to internal registers */	bd = (bd_t *)__res;	/* Allocate some private information.	*/	fep = (struct fec_enet_private *)kmalloc(sizeof(*fep), GFP_KERNEL);	if (fep == NULL)		return -ENOMEM;	__clear_user(fep,sizeof(*fep));	/* Create an Ethernet device instance.	*/	dev = init_etherdev(0, 0);	fecp = &(immap->im_cpm.cp_fec);	/* Whack a reset.  We should wait for this.	*/	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET;	for (i = 0;	     (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY);	     ++i) {		udelay(1);	}	if (i == FEC_RESET_DELAY) {		printk ("FEC Reset timeout!\n");	}	/* Set the Ethernet address.  If using multiple Enets on the 8xx,	 * this needs some work to get unique addresses.	 */	eap = (unsigned char *)my_enet_addr;	iap = bd->bi_enetaddr;#ifdef CONFIG_SCC_ENET	/*         * If a board has Ethernet configured both on a SCC and the         * FEC, it needs (at least) 2 MAC addresses (we know that Sun         * disagrees, but anyway). For the FEC port, we create         * another address by setting one of the address bits above         * something that would have (up to now) been allocated.	 */	for (i=0; i<6; i++)		tmpaddr[i] = *iap++;	tmpaddr[3] |= 0x80;	iap = tmpaddr;#endif	for (i=0; i<6; i++) {		dev->dev_addr[i] = *eap++ = *iap++;	}	/* Allocate memory for buffer descriptors.	*/	if (((RX_RING_SIZE + TX_RING_SIZE) * sizeof(cbd_t)) > PAGE_SIZE) {		printk("FEC init error.  Need more space.\n");		printk("FEC initialization failed.\n");		return 1;	}	mem_addr = __get_free_page(GFP_KERNEL);	cbd_base = (cbd_t *)mem_addr;	/* Make it uncached.	*/	pte = va_to_pte(mem_addr);	pte_val(*pte) |= _PAGE_NO_CACHE;	flush_tlb_page(init_mm.mmap, mem_addr);	/* Set receive and transmit descriptor base.	*/	fep->rx_bd_base = cbd_base;	fep->tx_bd_base = cbd_base + RX_RING_SIZE;	fep->skb_cur = fep->skb_dirty = 0;	/* Initialize the receive buffer descriptors.	*/	bdp = fep->rx_bd_base;	for (i=0; i<FEC_ENET_RX_PAGES; i++) {		/* Allocate a page.		*/		mem_addr = __get_free_page(GFP_KERNEL);		/* Make it uncached.		*/		pte = va_to_pte(mem_addr);		pte_val(*pte) |= _PAGE_NO_CACHE;		flush_tlb_page(init_mm.mmap, mem_addr);		/* Initialize the BD for every fragment in the page.		*/		for (j=0; j<FEC_ENET_RX_FRPPG; j++) {			bdp->cbd_sc = BD_ENET_RX_EMPTY;			bdp->cbd_bufaddr = __pa(mem_addr);			mem_addr += FEC_ENET_RX_FRSIZE;			bdp++;		}	}	/* Set the last buffer to wrap.	*/	bdp--;	bdp->cbd_sc |= BD_SC_WRAP;#ifdef CONFIG_FEC_PACKETHOOK	fep->ph_lock = 0;	fep->ph_rxhandler = fep->ph_txhandler = NULL;	fep->ph_proto = 0;	fep->ph_regaddr = NULL;	fep->ph_priv = NULL;#endif	/* Install our interrupt handler.	*/	if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0)		panic("Could not allocate FEC IRQ!");#ifdef CONFIG_RPXCLASSIC	/* Make Port C, bit 15 an input that causes interrupts.	*/	immap->im_ioport.iop_pcpar &= ~0x0001;	immap->im_ioport.iop_pcdir &= ~0x0001;	immap->im_ioport.iop_pcso  &= ~0x0001;	immap->im_ioport.iop_pcint |=  0x0001;	cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev);	/* Make LEDS reflect Link status.	*/	*((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE;#endif#ifdef PHY_INTERRUPT	if (request_8xxirq(PHY_INTERRUPT, mii_link_interrupt, 0, "mii", dev) != 0)		panic("Could not allocate MII IRQ!");	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |=		(0x80000000 >> PHY_INTERRUPT);#endif	dev->base_addr = (unsigned long)fecp;	dev->priv = fep;	/* The FEC Ethernet specific entries in the device structure. */	dev->open = fec_enet_open;	dev->hard_start_xmit = fec_enet_start_xmit;	dev->tx_timeout = fec_timeout;	dev->watchdog_timeo = TX_TIMEOUT;	dev->stop = fec_enet_close;	dev->get_stats = fec_enet_get_stats;	dev->set_multicast_list = set_multicast_list;#ifdef CONFIG_FEC_SUPPORT_SG	dev->features |= NETIF_F_SG;#endif#ifdef	CONFIG_USE_MDIO	for (i=0; i<NMII-1; i++)		mii_cmds[i].mii_next = &mii_cmds[i+1];	mii_free = mii_cmds;#endif	/* CONFIG_USE_MDIO */	/* Configure all of port D for MII.	*/	immap->im_ioport.iop_pdpar = 0x1fff;	/* Bits moved from Rev. D onward.	*/	if ((_get_IMMR() & 0xffff) < 0x0501)		immap->im_ioport.iop_pddir = 0x1c58;	/* Pre rev. D */	else		immap->im_ioport.iop_pddir = 0x1fff;	/* Rev. D and later */#ifdef	CONFIG_USE_MDIO	/* Set MII speed to 2.5 MHz	*/	fecp->fec_mii_speed = fep->phy_speed =		(		  ( ((bd->bi_intfreq * 1000000) + 500000) / 2500000 / 2 )		  & 0x3F		) << 1;#else	fecp->fec_mii_speed = 0;	/* turn off MDIO */#endif	/* CONFIG_USE_MDIO */	printk ("%s: FEC ENET Version 0.2, FEC irq %d"#ifdef PHY_INTERRUPT		", MII irq %d"#endif		", addr ",		dev->name, FEC_INTERRUPT#ifdef PHY_INTERRUPT		, PHY_INTERRUPT#endif	);	for (i=0; i<6; i++)		printk("%02x%c", dev->dev_addr[i], (i==5) ? '\n' : ':');#ifdef	CONFIG_USE_MDIO	/* start in full duplex mode, and negotiate speed */	fec_restart (dev, 1);#else			/* always use half duplex mode only */	fec_restart (dev, 0);#endif#ifdef	CONFIG_USE_MDIO	/* Queue up command to detect the PHY and initialize the	 * remainder of the interface.	 */	fep->phy_id_done = 0;	fep->phy_addr = 0;	mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);#endif	/* CONFIG_USE_MDIO */	return 0;}/* This function is called to start or restart the FEC during a link * change.  This only happens when switching between half and full * duplex. */static voidfec_restart(struct net_device *dev, int duplex){	struct fec_enet_private *fep;	int i;	volatile	cbd_t	*bdp;	volatile	immap_t	*immap;	volatile	fec_t	*fecp;	immap = (immap_t *)IMAP_ADDR;	/* pointer to internal registers */	fecp = &(immap->im_cpm.cp_fec);	fep = dev->priv;	/* Whack a reset.  We should wait for this.	*/	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET;	for (i = 0;	     (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY);	     ++i) {		udelay(1);	}	if (i == FEC_RESET_DELAY) {		printk ("FEC Reset timeout!\n");	}	/* Set station address.	*/	fecp->fec_addr_low  = (my_enet_addr[0] << 16) | my_enet_addr[1];	fecp->fec_addr_high =  my_enet_addr[2];	/* Reset all multicast.	*/	fecp->fec_hash_table_high = 0;	fecp->fec_hash_table_low  = 0;	/* Set maximum receive buffer size.	*/	fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;	fecp->fec_r_hash = PKT_MAXBUF_SIZE;	/* Set receive and transmit descriptor base.	*/	fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));	fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));	fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;	fep->cur_rx = fep->rx_bd_base;	/* Reset SKB transmit buffers.	*/	fep->skb_cur = fep->skb_dirty = 0;	for (i=0; i<=TX_RING_MOD_MASK; i++) {		if (fep->tx_skbuff[i] != NULL) {			dev_kfree_skb(fep->tx_skbuff[i]);			fep->tx_skbuff[i] = NULL;		}	}	/* Initialize the receive buffer descriptors.	*/	bdp = fep->rx_bd_base;	for (i=0; i<RX_RING_SIZE; i++) {		/* Initialize the BD for every fragment in the page.		*/		bdp->cbd_sc = BD_ENET_RX_EMPTY;		bdp++;	}	/* Set the last buffer to wrap.	*/	bdp--;	bdp->cbd_sc |= BD_SC_WRAP;	/* ...and the same for transmmit.	*/	bdp = fep->tx_bd_base;	for (i=0; i<TX_RING_SIZE; i++) {		/* Initialize the BD for every fragment in the page.		*/		bdp->cbd_sc = 0;		bdp->cbd_bufaddr = 0;		bdp++;	}	/* Set the last buffer to wrap.	*/	bdp--;	bdp->cbd_sc |= BD_SC_WRAP;	/* Enable MII mode.	*/	if (duplex) {		fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE;	/* MII enable */		fecp->fec_x_cntrl = FEC_TCNTRL_FDEN;		/* FD enable */	}	else {		fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT;		fecp->fec_x_cntrl = 0;	}	fep->full_duplex = duplex;	/* Enable big endian and don't care about SDMA FC.	*/	fecp->fec_fun_code = 0x78000000;#ifdef	CONFIG_USE_MDIO	/* Set MII speed.	*/	fecp->fec_mii_speed = fep->phy_speed;#endif	/* CONFIG_USE_MDIO */	/* Clear any outstanding interrupt.	*/	fecp->fec_ievent = 0xffc0;	fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;	/* Enable interrupts we wish to service.	*//* Joris: don't ask for TXB and RXB, we won't use them anyway.	fecp->fec_imask = ( FEC_ENET_TXF | FEC_ENET_TXB |			    FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII ); */	fecp->fec_imask = ( FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII );	/* And last, enable the transmit and receive processing.	*/	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN;	fecp->fec_r_des_active = 0x01000000;}static voidfec_stop(struct net_device *dev){	volatile	immap_t	*immap;	volatile	fec_t	*fecp;	struct fec_enet_private *fep;	int i;	immap = (immap_t *)IMAP_ADDR;	/* pointer to internal registers */	fecp = &(immap->im_cpm.cp_fec);	if ((fecp->fec_ecntrl & FEC_ECNTRL_ETHER_EN) == 0)		return;	/* already down */	fep = dev->priv;	fecp->fec_x_cntrl = 0x01;	/* Graceful transmit stop */	for (i = 0;	     ((fecp->fec_ievent & 0x10000000) == 0) && (i < FEC_RESET_DELAY);	     ++i) {		udelay(1);	}	if (i == FEC_RESET_DELAY) {		printk ("FEC timeout on graceful transmit stop\n");	}	/* Clear outstanding MII command interrupts.	*/	fecp->fec_ievent = FEC_ENET_MII;	/* Enable MII command finished interrupt	*/	fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;	fecp->fec_imask = FEC_ENET_MII;#ifdef	CONFIG_USE_MDIO	/* Set MII speed.	*/	fecp->fec_mii_speed = fep->phy_speed;#endif	/* CONFIG_USE_MDIO */	/* Disable FEC	*/	fecp->fec_ecntrl &= ~(FEC_ECNTRL_ETHER_EN);}

⌨️ 快捷键说明

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