eth_v10.c

来自「linux 内核源代码」· C语言 代码 · 共 1,954 行 · 第 1/4 页

C
1,954
字号
		}		skb_put(skb, length - ETHER_HEAD_LEN);        /* allocate room for the packet body */		skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */#ifdef ETHDEBUG		printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n",		       skb->head, skb->data, skb_tail_pointer(skb),		       skb_end_pointer(skb));		printk("copying packet to 0x%x.\n", skb_data_ptr);#endif		memcpy(skb_data_ptr, phys_to_virt(myNextRxDesc->descr.buf), length);	}	else {		/* Large packet, send directly to upper layers and allocate new		 * memory (aligned to cache line boundary to avoid bug).		 * Before sending the skb to upper layers we must make sure		 * that skb->data points to the aligned start of the packet.		 */		int align;		struct sk_buff *new_skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES);		if (!new_skb) {			np->stats.rx_errors++;			printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);			goto update_nextrxdesc;		}		skb = myNextRxDesc->skb;		align = (int)phys_to_virt(myNextRxDesc->descr.buf) - (int)skb->data;		skb_put(skb, length + align);		skb_pull(skb, align); /* Remove alignment bytes */		myNextRxDesc->skb = new_skb;		myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data));	}	skb->protocol = eth_type_trans(skb, dev);	/* Send the packet to the upper layers */	netif_rx(skb);  update_nextrxdesc:	/* Prepare for next packet */	myNextRxDesc->descr.status = 0;	prevRxDesc = myNextRxDesc;	myNextRxDesc = phys_to_virt(myNextRxDesc->descr.next);	rx_queue_len++;	/* Check if descriptors should be returned */	if (rx_queue_len == RX_QUEUE_THRESHOLD) {		flush_etrax_cache();		prevRxDesc->descr.ctrl |= d_eol;		myLastRxDesc->descr.ctrl &= ~d_eol;		myLastRxDesc = prevRxDesc;		rx_queue_len = 0;	}}/* The inverse routine to net_open(). */static inte100_close(struct net_device *dev){	struct net_local *np = netdev_priv(dev);	printk(KERN_INFO "Closing %s.\n", dev->name);	netif_stop_queue(dev);	*R_IRQ_MASK0_CLR =		IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |		IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |		IO_STATE(R_IRQ_MASK0_CLR, excessive_col, clr);	*R_IRQ_MASK2_CLR =		IO_STATE(R_IRQ_MASK2_CLR, dma0_descr, clr) |		IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |		IO_STATE(R_IRQ_MASK2_CLR, dma1_descr, clr) |		IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);	/* Stop the receiver and the transmitter */	RESET_DMA(NETWORK_TX_DMA_NBR);	RESET_DMA(NETWORK_RX_DMA_NBR);	/* Flush the Tx and disable Rx here. */	free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);	free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);	free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);	cris_free_dma(NETWORK_TX_DMA_NBR, cardname);	cris_free_dma(NETWORK_RX_DMA_NBR, cardname);	/* Update the statistics here. */	update_rx_stats(&np->stats);	update_tx_stats(&np->stats);	/* Stop speed/duplex timers */	del_timer(&speed_timer);	del_timer(&duplex_timer);	return 0;}static inte100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){	struct mii_ioctl_data *data = if_mii(ifr);	struct net_local *np = netdev_priv(dev);	int rc = 0;        int old_autoneg;	spin_lock(&np->lock); /* Preempt protection */	switch (cmd) {		/* The ioctls below should be considered obsolete but are */		/* still present for compatability with old scripts/apps  */		case SET_ETH_SPEED_10:                  /* 10 Mbps */			e100_set_speed(dev, 10);			break;		case SET_ETH_SPEED_100:                /* 100 Mbps */			e100_set_speed(dev, 100);			break;		case SET_ETH_SPEED_AUTO:        /* Auto-negotiate speed */			e100_set_speed(dev, 0);			break;		case SET_ETH_DUPLEX_HALF:       /* Half duplex */			e100_set_duplex(dev, half);			break;		case SET_ETH_DUPLEX_FULL:       /* Full duplex */			e100_set_duplex(dev, full);			break;		case SET_ETH_DUPLEX_AUTO:       /* Auto-negotiate duplex */			e100_set_duplex(dev, autoneg);			break;	        case SET_ETH_AUTONEG:			old_autoneg = autoneg_normal;		        autoneg_normal = *(int*)data;			if (autoneg_normal != old_autoneg)				e100_negotiate(dev);			break;		default:			rc = generic_mii_ioctl(&np->mii_if, if_mii(ifr),						cmd, NULL);			break;	}	spin_unlock(&np->lock);	return rc;}static int e100_get_settings(struct net_device *dev,			     struct ethtool_cmd *cmd){	struct net_local *np = netdev_priv(dev);	int err;	spin_lock_irq(&np->lock);	err = mii_ethtool_gset(&np->mii_if, cmd);	spin_unlock_irq(&np->lock);	/* The PHY may support 1000baseT, but the Etrax100 does not.  */	cmd->supported &= ~(SUPPORTED_1000baseT_Half			    | SUPPORTED_1000baseT_Full);	return err;}static int e100_set_settings(struct net_device *dev,			     struct ethtool_cmd *ecmd){	if (ecmd->autoneg == AUTONEG_ENABLE) {		e100_set_duplex(dev, autoneg);		e100_set_speed(dev, 0);	} else {		e100_set_duplex(dev, ecmd->duplex == DUPLEX_HALF ? half : full);		e100_set_speed(dev, ecmd->speed == SPEED_10 ? 10: 100);	}	return 0;}static void e100_get_drvinfo(struct net_device *dev,			     struct ethtool_drvinfo *info){	strncpy(info->driver, "ETRAX 100LX", sizeof(info->driver) - 1);	strncpy(info->version, "$Revision: 1.31 $", sizeof(info->version) - 1);	strncpy(info->fw_version, "N/A", sizeof(info->fw_version) - 1);	strncpy(info->bus_info, "N/A", sizeof(info->bus_info) - 1);}static int e100_nway_reset(struct net_device *dev){	if (current_duplex == autoneg && current_speed_selection == 0)		e100_negotiate(dev);	return 0;}static const struct ethtool_ops e100_ethtool_ops = {	.get_settings	= e100_get_settings,	.set_settings	= e100_set_settings,	.get_drvinfo	= e100_get_drvinfo,	.nway_reset	= e100_nway_reset,	.get_link	= ethtool_op_get_link,};static inte100_set_config(struct net_device *dev, struct ifmap *map){	struct net_local *np = netdev_priv(dev);	spin_lock(&np->lock); /* Preempt protection */	switch(map->port) {		case IF_PORT_UNKNOWN:			/* Use autoneg */			e100_set_speed(dev, 0);			e100_set_duplex(dev, autoneg);			break;		case IF_PORT_10BASET:			e100_set_speed(dev, 10);			e100_set_duplex(dev, autoneg);			break;		case IF_PORT_100BASET:		case IF_PORT_100BASETX:			e100_set_speed(dev, 100);			e100_set_duplex(dev, autoneg);			break;		case IF_PORT_100BASEFX:		case IF_PORT_10BASE2:		case IF_PORT_AUI:			spin_unlock(&np->lock);			return -EOPNOTSUPP;			break;		default:			printk(KERN_ERR "%s: Invalid media selected", dev->name);			spin_unlock(&np->lock);			return -EINVAL;	}	spin_unlock(&np->lock);	return 0;}static voidupdate_rx_stats(struct net_device_stats *es){	unsigned long r = *R_REC_COUNTERS;	/* update stats relevant to reception errors */	es->rx_fifo_errors += IO_EXTRACT(R_REC_COUNTERS, congestion, r);	es->rx_crc_errors += IO_EXTRACT(R_REC_COUNTERS, crc_error, r);	es->rx_frame_errors += IO_EXTRACT(R_REC_COUNTERS, alignment_error, r);	es->rx_length_errors += IO_EXTRACT(R_REC_COUNTERS, oversize, r);}static voidupdate_tx_stats(struct net_device_stats *es){	unsigned long r = *R_TR_COUNTERS;	/* update stats relevant to transmission errors */	es->collisions +=		IO_EXTRACT(R_TR_COUNTERS, single_col, r) +		IO_EXTRACT(R_TR_COUNTERS, multiple_col, r);}/* * Get the current statistics. * This may be called with the card open or closed. */static struct net_device_stats *e100_get_stats(struct net_device *dev){	struct net_local *lp = netdev_priv(dev);	unsigned long flags;	spin_lock_irqsave(&lp->lock, flags);	update_rx_stats(&lp->stats);	update_tx_stats(&lp->stats);	spin_unlock_irqrestore(&lp->lock, flags);	return &lp->stats;}/* * Set or clear the multicast filter for this adaptor. * num_addrs == -1	Promiscuous mode, receive all packets * num_addrs == 0	Normal mode, clear multicast list * num_addrs > 0	Multicast mode, receive normal and MC packets, *			and do best-effort filtering. */static voidset_multicast_list(struct net_device *dev){	struct net_local *lp = netdev_priv(dev);	int num_addr = dev->mc_count;	unsigned long int lo_bits;	unsigned long int hi_bits;	spin_lock(&lp->lock);	if (dev->flags & IFF_PROMISC) {		/* promiscuous mode */		lo_bits = 0xfffffffful;		hi_bits = 0xfffffffful;		/* Enable individual receive */		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive);		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;	} else if (dev->flags & IFF_ALLMULTI) {		/* enable all multicasts */		lo_bits = 0xfffffffful;		hi_bits = 0xfffffffful;		/* Disable individual receive */		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;	} else if (num_addr == 0) {		/* Normal, clear the mc list */		lo_bits = 0x00000000ul;		hi_bits = 0x00000000ul;		/* Disable individual receive */		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;	} else {		/* MC mode, receive normal and MC packets */		char hash_ix;		struct dev_mc_list *dmi = dev->mc_list;		int i;		char *baddr;		lo_bits = 0x00000000ul;		hi_bits = 0x00000000ul;		for (i = 0; i < num_addr; i++) {			/* Calculate the hash index for the GA registers */			hash_ix = 0;			baddr = dmi->dmi_addr;			hash_ix ^= (*baddr) & 0x3f;			hash_ix ^= ((*baddr) >> 6) & 0x03;			++baddr;			hash_ix ^= ((*baddr) << 2) & 0x03c;			hash_ix ^= ((*baddr) >> 4) & 0xf;			++baddr;			hash_ix ^= ((*baddr) << 4) & 0x30;			hash_ix ^= ((*baddr) >> 2) & 0x3f;			++baddr;			hash_ix ^= (*baddr) & 0x3f;			hash_ix ^= ((*baddr) >> 6) & 0x03;			++baddr;			hash_ix ^= ((*baddr) << 2) & 0x03c;			hash_ix ^= ((*baddr) >> 4) & 0xf;			++baddr;			hash_ix ^= ((*baddr) << 4) & 0x30;			hash_ix ^= ((*baddr) >> 2) & 0x3f;			hash_ix &= 0x3f;			if (hash_ix >= 32) {				hi_bits |= (1 << (hash_ix-32));			} else {				lo_bits |= (1 << hash_ix);			}			dmi = dmi->next;		}		/* Disable individual receive */		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;	}	*R_NETWORK_GA_0 = lo_bits;	*R_NETWORK_GA_1 = hi_bits;	spin_unlock(&lp->lock);}voide100_hardware_send_packet(struct net_local *np, char *buf, int length){	D(printk("e100 send pack, buf 0x%x len %d\n", buf, length));	spin_lock(&np->led_lock);	if (!led_active && time_after(jiffies, led_next_time)) {		/* light the network leds depending on the current speed. */		e100_set_network_leds(NETWORK_ACTIVITY);		/* Set the earliest time we may clear the LED */		led_next_time = jiffies + NET_FLASH_TIME;		led_active = 1;		mod_timer(&clear_led_timer, jiffies + HZ/10);	}	spin_unlock(&np->led_lock);	/* configure the tx dma descriptor */	myNextTxDesc->descr.sw_len = length;	myNextTxDesc->descr.ctrl = d_eop | d_eol | d_wait;	myNextTxDesc->descr.buf = virt_to_phys(buf);        /* Move end of list */        myLastTxDesc->descr.ctrl &= ~d_eol;        myLastTxDesc = myNextTxDesc;	/* Restart DMA channel */	*R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, restart);}static voide100_clear_network_leds(unsigned long dummy){	struct net_device *dev = (struct net_device *)dummy;	struct net_local *np = netdev_priv(dev);	spin_lock(&np->led_lock);	if (led_active && time_after(jiffies, led_next_time)) {		e100_set_network_leds(NO_NETWORK_ACTIVITY);		/* Set the earliest time we may set the LED */		led_next_time = jiffies + NET_FLASH_PAUSE;		led_active = 0;	}	spin_unlock(&np->led_lock);}static voide100_set_network_leds(int active){#if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK)	int light_leds = (active == NO_NETWORK_ACTIVITY);#elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY)	int light_leds = (active == NETWORK_ACTIVITY);#else#error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY"#endif	if (!current_speed) {		/* Make LED red, link is down */#if defined(CONFIG_ETRAX_NETWORK_RED_ON_NO_CONNECTION)		LED_NETWORK_SET(LED_RED);#else		LED_NETWORK_SET(LED_OFF);#endif	} else if (light_leds) {		if (current_speed == 10) {			LED_NETWORK_SET(LED_ORANGE);		} else {			LED_NETWORK_SET(LED_GREEN);		}	} else {		LED_NETWORK_SET(LED_OFF);	}}#ifdef CONFIG_NET_POLL_CONTROLLERstatic voide100_netpoll(struct net_device* netdev){	e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev, NULL);}#endifstatic intetrax_init_module(void){	return etrax_ethernet_init();}static int __inite100_boot_setup(char* str){	struct sockaddr sa = {0};	int i;	/* Parse the colon separated Ethernet station address */	for (i = 0; i <  ETH_ALEN; i++) {		unsigned int tmp;		if (sscanf(str + 3*i, "%2x", &tmp) != 1) {			printk(KERN_WARNING "Malformed station address");			return 0;		}		sa.sa_data[i] = (char)tmp;	}	default_mac = sa;	return 1;}__setup("etrax100_eth=", e100_boot_setup);module_init(etrax_init_module);

⌨️ 快捷键说明

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