mv643xx_eth.c

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

C
2,216
字号
	mv_read (INTERRUPT_CAUSE_EXTEND_REG(port_num));	err = request_irq(dev->irq, mv643xx_eth_int_handler,			IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, dev);	if (err) {		printk(KERN_ERR "Can not assign IRQ number to MV643XX_eth%d\n",								port_num);		return -EAGAIN;	}	eth_port_init(mp);	memset(&mp->timeout, 0, sizeof(struct timer_list));	mp->timeout.function = mv643xx_eth_rx_refill_descs_timer_wrapper;	mp->timeout.data = (unsigned long)dev;	/* Allocate RX and TX skb rings */	mp->rx_skb = kmalloc(sizeof(*mp->rx_skb) * mp->rx_ring_size,								GFP_KERNEL);	if (!mp->rx_skb) {		printk(KERN_ERR "%s: Cannot allocate Rx skb ring\n", dev->name);		err = -ENOMEM;		goto out_free_irq;	}	mp->tx_skb = kmalloc(sizeof(*mp->tx_skb) * mp->tx_ring_size,								GFP_KERNEL);	if (!mp->tx_skb) {		printk(KERN_ERR "%s: Cannot allocate Tx skb ring\n", dev->name);		err = -ENOMEM;		goto out_free_rx_skb;	}	/* Allocate TX ring */	mp->tx_desc_count = 0;	size = mp->tx_ring_size * sizeof(struct eth_tx_desc);	mp->tx_desc_area_size = size;	if (mp->tx_sram_size) {		mp->p_tx_desc_area = ioremap(mp->tx_sram_addr,							mp->tx_sram_size);		mp->tx_desc_dma = mp->tx_sram_addr;	} else		mp->p_tx_desc_area = dma_alloc_coherent(NULL, size,							&mp->tx_desc_dma,							GFP_KERNEL);	if (!mp->p_tx_desc_area) {		printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",							dev->name, size);		err = -ENOMEM;		goto out_free_tx_skb;	}	BUG_ON((u32) mp->p_tx_desc_area & 0xf);	/* check 16-byte alignment */	memset((void *)mp->p_tx_desc_area, 0, mp->tx_desc_area_size);	ether_init_tx_desc_ring(mp);	/* Allocate RX ring */	mp->rx_desc_count = 0;	size = mp->rx_ring_size * sizeof(struct eth_rx_desc);	mp->rx_desc_area_size = size;	if (mp->rx_sram_size) {		mp->p_rx_desc_area = ioremap(mp->rx_sram_addr,							mp->rx_sram_size);		mp->rx_desc_dma = mp->rx_sram_addr;	} else		mp->p_rx_desc_area = dma_alloc_coherent(NULL, size,							&mp->rx_desc_dma,							GFP_KERNEL);	if (!mp->p_rx_desc_area) {		printk(KERN_ERR "%s: Cannot allocate Rx ring (size %d bytes)\n",							dev->name, size);		printk(KERN_ERR "%s: Freeing previously allocated TX queues...",							dev->name);		if (mp->rx_sram_size)			iounmap(mp->p_tx_desc_area);		else			dma_free_coherent(NULL, mp->tx_desc_area_size,					mp->p_tx_desc_area, mp->tx_desc_dma);		err = -ENOMEM;		goto out_free_tx_skb;	}	memset((void *)mp->p_rx_desc_area, 0, size);	ether_init_rx_desc_ring(mp);	mv643xx_eth_rx_refill_descs(dev);	/* Fill RX ring with skb's */#ifdef MV643XX_NAPI	napi_enable(&mp->napi);#endif	eth_port_start(dev);	/* Interrupt Coalescing */#ifdef MV643XX_COAL	mp->rx_int_coal =		eth_port_set_rx_coal(port_num, 133000000, MV643XX_RX_COAL);#endif	mp->tx_int_coal =		eth_port_set_tx_coal(port_num, 133000000, MV643XX_TX_COAL);	/* Unmask phy and link status changes interrupts */	mv_write(INTERRUPT_EXTEND_MASK_REG(port_num), ETH_INT_UNMASK_ALL_EXT);	/* Unmask RX buffer and TX end interrupt */	mv_write(INTERRUPT_MASK_REG(port_num), ETH_INT_UNMASK_ALL);	return 0;out_free_tx_skb:	kfree(mp->tx_skb);out_free_rx_skb:	kfree(mp->rx_skb);out_free_irq:	free_irq(dev->irq, dev);	return err;}static void mv643xx_eth_free_tx_rings(struct net_device *dev){	struct mv643xx_private *mp = netdev_priv(dev);	/* Stop Tx Queues */	mv643xx_eth_port_disable_tx(mp->port_num);	/* Free outstanding skb's on TX ring */	mv643xx_eth_free_all_tx_descs(dev);	BUG_ON(mp->tx_used_desc_q != mp->tx_curr_desc_q);	/* Free TX ring */	if (mp->tx_sram_size)		iounmap(mp->p_tx_desc_area);	else		dma_free_coherent(NULL, mp->tx_desc_area_size,				mp->p_tx_desc_area, mp->tx_desc_dma);}static void mv643xx_eth_free_rx_rings(struct net_device *dev){	struct mv643xx_private *mp = netdev_priv(dev);	unsigned int port_num = mp->port_num;	int curr;	/* Stop RX Queues */	mv643xx_eth_port_disable_rx(port_num);	/* Free preallocated skb's on RX rings */	for (curr = 0; mp->rx_desc_count && curr < mp->rx_ring_size; curr++) {		if (mp->rx_skb[curr]) {			dev_kfree_skb(mp->rx_skb[curr]);			mp->rx_desc_count--;		}	}	if (mp->rx_desc_count)		printk(KERN_ERR			"%s: Error in freeing Rx Ring. %d skb's still"			" stuck in RX Ring - ignoring them\n", dev->name,			mp->rx_desc_count);	/* Free RX ring */	if (mp->rx_sram_size)		iounmap(mp->p_rx_desc_area);	else		dma_free_coherent(NULL, mp->rx_desc_area_size,				mp->p_rx_desc_area, mp->rx_desc_dma);}/* * mv643xx_eth_stop * * This function is used when closing the network device. * It updates the hardware, * release all memory that holds buffers and descriptors and release the IRQ. * Input :	a pointer to the device structure * Output :	zero if success , nonzero if fails */static int mv643xx_eth_stop(struct net_device *dev){	struct mv643xx_private *mp = netdev_priv(dev);	unsigned int port_num = mp->port_num;	/* Mask all interrupts on ethernet port */	mv_write(INTERRUPT_MASK_REG(port_num), ETH_INT_MASK_ALL);	/* wait for previous write to complete */	mv_read(INTERRUPT_MASK_REG(port_num));#ifdef MV643XX_NAPI	napi_disable(&mp->napi);#endif	netif_carrier_off(dev);	netif_stop_queue(dev);	eth_port_reset(mp->port_num);	mv643xx_eth_free_tx_rings(dev);	mv643xx_eth_free_rx_rings(dev);	free_irq(dev->irq, dev);	return 0;}#ifdef MV643XX_NAPI/* * mv643xx_poll * * This function is used in case of NAPI */static int mv643xx_poll(struct napi_struct *napi, int budget){	struct mv643xx_private *mp = container_of(napi, struct mv643xx_private, napi);	struct net_device *dev = mp->dev;	unsigned int port_num = mp->port_num;	int work_done;#ifdef MV643XX_TX_FAST_REFILL	if (++mp->tx_clean_threshold > 5) {		mv643xx_eth_free_completed_tx_descs(dev);		mp->tx_clean_threshold = 0;	}#endif	work_done = 0;	if ((mv_read(RX_CURRENT_QUEUE_DESC_PTR_0(port_num)))	    != (u32) mp->rx_used_desc_q)		work_done = mv643xx_eth_receive_queue(dev, budget);	if (work_done < budget) {		netif_rx_complete(dev, napi);		mv_write(INTERRUPT_CAUSE_REG(port_num), 0);		mv_write(INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);		mv_write(INTERRUPT_MASK_REG(port_num), ETH_INT_UNMASK_ALL);	}	return work_done;}#endif/** * has_tiny_unaligned_frags - check if skb has any small, unaligned fragments * * Hardware can't handle unaligned fragments smaller than 9 bytes. * This helper function detects that case. */static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb){	unsigned int frag;	skb_frag_t *fragp;	for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {		fragp = &skb_shinfo(skb)->frags[frag];		if (fragp->size <= 8 && fragp->page_offset & 0x7)			return 1;	}	return 0;}/** * eth_alloc_tx_desc_index - return the index of the next available tx desc */static int eth_alloc_tx_desc_index(struct mv643xx_private *mp){	int tx_desc_curr;	BUG_ON(mp->tx_desc_count >= mp->tx_ring_size);	tx_desc_curr = mp->tx_curr_desc_q;	mp->tx_curr_desc_q = (tx_desc_curr + 1) % mp->tx_ring_size;	BUG_ON(mp->tx_curr_desc_q == mp->tx_used_desc_q);	return tx_desc_curr;}/** * eth_tx_fill_frag_descs - fill tx hw descriptors for an skb's fragments. * * Ensure the data for each fragment to be transmitted is mapped properly, * then fill in descriptors in the tx hw queue. */static void eth_tx_fill_frag_descs(struct mv643xx_private *mp,				   struct sk_buff *skb){	int frag;	int tx_index;	struct eth_tx_desc *desc;	for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {		skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];		tx_index = eth_alloc_tx_desc_index(mp);		desc = &mp->p_tx_desc_area[tx_index];		desc->cmd_sts = ETH_BUFFER_OWNED_BY_DMA;		/* Last Frag enables interrupt and frees the skb */		if (frag == (skb_shinfo(skb)->nr_frags - 1)) {			desc->cmd_sts |= ETH_ZERO_PADDING |					 ETH_TX_LAST_DESC |					 ETH_TX_ENABLE_INTERRUPT;			mp->tx_skb[tx_index] = skb;		} else			mp->tx_skb[tx_index] = NULL;		desc = &mp->p_tx_desc_area[tx_index];		desc->l4i_chk = 0;		desc->byte_cnt = this_frag->size;		desc->buf_ptr = dma_map_page(NULL, this_frag->page,						this_frag->page_offset,						this_frag->size,						DMA_TO_DEVICE);	}}/** * eth_tx_submit_descs_for_skb - submit data from an skb to the tx hw * * Ensure the data for an skb to be transmitted is mapped properly, * then fill in descriptors in the tx hw queue and start the hardware. */static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp,					struct sk_buff *skb){	int tx_index;	struct eth_tx_desc *desc;	u32 cmd_sts;	int length;	int nr_frags = skb_shinfo(skb)->nr_frags;	cmd_sts = ETH_TX_FIRST_DESC | ETH_GEN_CRC | ETH_BUFFER_OWNED_BY_DMA;	tx_index = eth_alloc_tx_desc_index(mp);	desc = &mp->p_tx_desc_area[tx_index];	if (nr_frags) {		eth_tx_fill_frag_descs(mp, skb);		length = skb_headlen(skb);		mp->tx_skb[tx_index] = NULL;	} else {		cmd_sts |= ETH_ZERO_PADDING |			   ETH_TX_LAST_DESC |			   ETH_TX_ENABLE_INTERRUPT;		length = skb->len;		mp->tx_skb[tx_index] = skb;	}	desc->byte_cnt = length;	desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);	if (skb->ip_summed == CHECKSUM_PARTIAL) {		BUG_ON(skb->protocol != ETH_P_IP);		cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM |			   ETH_GEN_IP_V_4_CHECKSUM  |			   ip_hdr(skb)->ihl << ETH_TX_IHL_SHIFT;		switch (ip_hdr(skb)->protocol) {		case IPPROTO_UDP:			cmd_sts |= ETH_UDP_FRAME;			desc->l4i_chk = udp_hdr(skb)->check;			break;		case IPPROTO_TCP:			desc->l4i_chk = tcp_hdr(skb)->check;			break;		default:			BUG();		}	} else {		/* Errata BTS #50, IHL must be 5 if no HW checksum */		cmd_sts |= 5 << ETH_TX_IHL_SHIFT;		desc->l4i_chk = 0;	}	/* ensure all other descriptors are written before first cmd_sts */	wmb();	desc->cmd_sts = cmd_sts;	/* ensure all descriptors are written before poking hardware */	wmb();	mv643xx_eth_port_enable_tx(mp->port_num, ETH_TX_QUEUES_ENABLED);	mp->tx_desc_count += nr_frags + 1;}/** * mv643xx_eth_start_xmit - queue an skb to the hardware for transmission * */static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct mv643xx_private *mp = netdev_priv(dev);	struct net_device_stats *stats = &dev->stats;	unsigned long flags;	BUG_ON(netif_queue_stopped(dev));	BUG_ON(skb == NULL);	if (mp->tx_ring_size - mp->tx_desc_count < MAX_DESCS_PER_SKB) {		printk(KERN_ERR "%s: transmit with queue full\n", dev->name);		netif_stop_queue(dev);		return 1;	}	if (has_tiny_unaligned_frags(skb)) {		if (__skb_linearize(skb)) {			stats->tx_dropped++;			printk(KERN_DEBUG "%s: failed to linearize tiny "					"unaligned fragment\n", dev->name);			return 1;		}	}	spin_lock_irqsave(&mp->lock, flags);	eth_tx_submit_descs_for_skb(mp, skb);	stats->tx_bytes += skb->len;	stats->tx_packets++;	dev->trans_start = jiffies;	if (mp->tx_ring_size - mp->tx_desc_count < MAX_DESCS_PER_SKB)		netif_stop_queue(dev);	spin_unlock_irqrestore(&mp->lock, flags);	return 0;		/* success */}#ifdef CONFIG_NET_POLL_CONTROLLERstatic void mv643xx_netpoll(struct net_device *netdev){	struct mv643xx_private *mp = netdev_priv(netdev);	int port_num = mp->port_num;	mv_write(INTERRUPT_MASK_REG(port_num), ETH_INT_MASK_ALL);	/* wait for previous write to complete */

⌨️ 快捷键说明

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