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 + -
显示快捷键?