mv643xx_eth.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,225 行 · 第 1/5 页
C
2,225 行
/* * drivers/net/mv64340_eth.c - Driver for MV64340X ethernet ports * Copyright (C) 2002 Matthew Dharm <mdharm@momenco.com> * * Based on the 64360 driver from: * Copyright (C) 2002 rabeeh@galileo.co.il * * Copyright (C) 2003 PMC-Sierra, Inc., * written by Manish Lachwani (lachwani@pmc-sierra.com) * * Copyright (C) 2003 Ralf Baechle <ralf@linux-mips.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/config.h>#include <linux/sched.h>#include <linux/ptrace.h>#include <linux/fcntl.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/ip.h>#include <linux/init.h>#include <linux/in.h>#include <linux/pci.h>#include <linux/workqueue.h>#include <asm/smp.h>#include <linux/skbuff.h>#include <linux/tcp.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <net/ip.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/types.h>#include <asm/pgtable.h>#include <asm/system.h>#include "mv643xx_eth.h"/* * The first part is the high level driver of the gigE ethernet ports. *//* Definition for configuring driver */#undef MV64340_RX_QUEUE_FILL_ON_TASK/* Constants */#define EXTRA_BYTES 32#define WRAP ETH_HLEN + 2 + 4 + 16#define BUFFER_MTU dev->mtu + WRAP#define INT_CAUSE_UNMASK_ALL 0x0007ffff#define INT_CAUSE_UNMASK_ALL_EXT 0x0011ffff#ifdef MV64340_RX_FILL_ON_TASK#define INT_CAUSE_MASK_ALL 0x00000000#define INT_CAUSE_CHECK_BITS INT_CAUSE_UNMASK_ALL#define INT_CAUSE_CHECK_BITS_EXT INT_CAUSE_UNMASK_ALL_EXT#endif/* Static function declarations */static int mv64340_eth_real_open(struct net_device *);static int mv64340_eth_real_stop(struct net_device *);static int mv64340_eth_change_mtu(struct net_device *, int);static struct net_device_stats *mv64340_eth_get_stats(struct net_device *);static void eth_port_init_mac_tables(unsigned int eth_port_num);#ifdef MV64340_NAPIstatic int mv64340_poll(struct net_device *dev, int *budget);#endifunsigned char prom_mac_addr_base[6];unsigned long mv64340_sram_base;/* * Changes MTU (maximum transfer unit) of the gigabit ethenret port * * Input : pointer to ethernet interface network device structure * new mtu size * Output : 0 upon success, -EINVAL upon failure */static int mv64340_eth_change_mtu(struct net_device *dev, int new_mtu){ struct mv64340_private *mp = netdev_priv(dev); unsigned long flags; spin_lock_irqsave(&mp->lock, flags); if ((new_mtu > 9500) || (new_mtu < 64)) { spin_unlock_irqrestore(&mp->lock, flags); return -EINVAL; } dev->mtu = new_mtu; /* * Stop then re-open the interface. This will allocate RX skb's with * the new MTU. * There is a possible danger that the open will not successed, due * to memory is full, which might fail the open function. */ if (netif_running(dev)) { if (mv64340_eth_real_stop(dev)) printk(KERN_ERR "%s: Fatal error on stopping device\n", dev->name); if (mv64340_eth_real_open(dev)) printk(KERN_ERR "%s: Fatal error on opening device\n", dev->name); } spin_unlock_irqrestore(&mp->lock, flags); return 0;}/* * mv64340_eth_rx_task * * Fills / refills RX queue on a certain gigabit ethernet port * * Input : pointer to ethernet interface network device structure * Output : N/A */static void mv64340_eth_rx_task(void *data){ struct net_device *dev = (struct net_device *) data; struct mv64340_private *mp = netdev_priv(dev); struct pkt_info pkt_info; struct sk_buff *skb; if (test_and_set_bit(0, &mp->rx_task_busy)) panic("%s: Error in test_set_bit / clear_bit", dev->name); while (mp->rx_ring_skbs < (mp->rx_ring_size - 5)) { /* The +8 for buffer allignment and another 32 byte extra */ skb = dev_alloc_skb(BUFFER_MTU + 8 + EXTRA_BYTES); if (!skb) /* Better luck next time */ break; mp->rx_ring_skbs++; pkt_info.cmd_sts = ETH_RX_ENABLE_INTERRUPT; pkt_info.byte_cnt = dev->mtu + ETH_HLEN + 4 + 2 + EXTRA_BYTES; /* Allign buffer to 8 bytes */ if (pkt_info.byte_cnt & ~0x7) { pkt_info.byte_cnt &= ~0x7; pkt_info.byte_cnt += 8; } pkt_info.buf_ptr = pci_map_single(0, skb->data, dev->mtu + ETH_HLEN + 4 + 2 + EXTRA_BYTES, PCI_DMA_FROMDEVICE); pkt_info.return_info = skb; if (eth_rx_return_buff(mp, &pkt_info) != ETH_OK) { printk(KERN_ERR "%s: Error allocating RX Ring\n", dev->name); break; } skb_reserve(skb, 2); } clear_bit(0, &mp->rx_task_busy); /* * If RX ring is empty of SKB, set a timer to try allocating * again in a later time . */ if ((mp->rx_ring_skbs == 0) && (mp->rx_timer_flag == 0)) { printk(KERN_INFO "%s: Rx ring is empty\n", dev->name); /* After 100mSec */ mp->timeout.expires = jiffies + (HZ / 10); add_timer(&mp->timeout); mp->rx_timer_flag = 1; }#if MV64340_RX_QUEUE_FILL_ON_TASK else { /* Return interrupts */ MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(mp->port_num), INT_CAUSE_UNMASK_ALL); }#endif}/* * mv64340_eth_rx_task_timer_wrapper * * Timer routine to wake up RX queue filling task. This function is * used only in case the RX queue is empty, and all alloc_skb has * failed (due to out of memory event). * * Input : pointer to ethernet interface network device structure * Output : N/A */static void mv64340_eth_rx_task_timer_wrapper(unsigned long data){ struct net_device *dev = (struct net_device *) data; struct mv64340_private *mp = netdev_priv(dev); mp->rx_timer_flag = 0; mv64340_eth_rx_task((void *) data);}/* * mv64340_eth_update_mac_address * * Update the MAC address of the port in the address table * * Input : pointer to ethernet interface network device structure * Output : N/A */static void mv64340_eth_update_mac_address(struct net_device *dev){ struct mv64340_private *mp = netdev_priv(dev); unsigned int port_num = mp->port_num; eth_port_init_mac_tables(port_num); memcpy(mp->port_mac_addr, dev->dev_addr, 6); eth_port_uc_addr_set(port_num, mp->port_mac_addr);}/* * mv64340_eth_set_rx_mode * * Change from promiscuos to regular rx mode * * Input : pointer to ethernet interface network device structure * Output : N/A */static void mv64340_eth_set_rx_mode(struct net_device *dev){ struct mv64340_private *mp = netdev_priv(dev); if (dev->flags & IFF_PROMISC) { ethernet_set_config_reg (mp->port_num, ethernet_get_config_reg(mp->port_num) | ETH_UNICAST_PROMISCUOUS_MODE); } else { ethernet_set_config_reg (mp->port_num, ethernet_get_config_reg(mp->port_num) & ~(unsigned int) ETH_UNICAST_PROMISCUOUS_MODE); }}/* * mv64340_eth_set_mac_address * * Change the interface's mac address. * No special hardware thing should be done because interface is always * put in promiscuous mode. * * Input : pointer to ethernet interface network device structure and * a pointer to the designated entry to be added to the cache. * Output : zero upon success, negative upon failure */static int mv64340_eth_set_mac_address(struct net_device *dev, void *addr){ int i; for (i = 0; i < 6; i++) /* +2 is for the offset of the HW addr type */ dev->dev_addr[i] = ((unsigned char *) addr)[i + 2]; mv64340_eth_update_mac_address(dev); return 0;}/* * mv64340_eth_tx_timeout * * Called upon a timeout on transmitting a packet * * Input : pointer to ethernet interface network device structure. * Output : N/A */static void mv64340_eth_tx_timeout(struct net_device *dev){ struct mv64340_private *mp = netdev_priv(dev); printk(KERN_INFO "%s: TX timeout ", dev->name); /* Do the reset outside of interrupt context */ schedule_work(&mp->tx_timeout_task);}/* * mv64340_eth_tx_timeout_task * * Actual routine to reset the adapter when a timeout on Tx has occurred */static void mv64340_eth_tx_timeout_task(struct net_device *dev){ struct mv64340_private *mp = netdev_priv(dev); netif_device_detach(dev); eth_port_reset(mp->port_num); eth_port_start(mp); netif_device_attach(dev);}/* * mv64340_eth_free_tx_queue * * Input : dev - a pointer to the required interface * * Output : 0 if was able to release skb , nonzero otherwise */static int mv64340_eth_free_tx_queue(struct net_device *dev, unsigned int eth_int_cause_ext){ struct mv64340_private *mp = netdev_priv(dev); struct net_device_stats *stats = &mp->stats; struct pkt_info pkt_info; int released = 1; if (!(eth_int_cause_ext & (BIT0 | BIT8))) return released; spin_lock(&mp->lock); /* Check only queue 0 */ while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) { if (pkt_info.cmd_sts & BIT0) { printk("%s: Error in TX\n", dev->name); stats->tx_errors++; } /* * If return_info is different than 0, release the skb. * The case where return_info is not 0 is only in case * when transmitted a scatter/gather packet, where only * last skb releases the whole chain. */ if (pkt_info.return_info) { dev_kfree_skb_irq((struct sk_buff *) pkt_info.return_info); released = 0; if (skb_shinfo(pkt_info.return_info)->nr_frags) pci_unmap_page(NULL, pkt_info.buf_ptr, pkt_info.byte_cnt, PCI_DMA_TODEVICE); if (mp->tx_ring_skbs != 1) mp->tx_ring_skbs--; } else pci_unmap_page(NULL, pkt_info.buf_ptr, pkt_info.byte_cnt, PCI_DMA_TODEVICE); /* * Decrement the number of outstanding skbs counter on * the TX queue. */ if (mp->tx_ring_skbs == 0) panic("ERROR - TX outstanding SKBs counter is corrupted"); } spin_unlock(&mp->lock); return released;}/* * mv64340_eth_receive * * This function is forward packets that are received from the port's * queues toward kernel core or FastRoute them to another interface. * * Input : dev - a pointer to the required interface * max - maximum number to receive (0 means unlimted) * * Output : number of served packets */#ifdef MV64340_NAPIstatic int mv64340_eth_receive_queue(struct net_device *dev, unsigned int max, int budget)#elsestatic int mv64340_eth_receive_queue(struct net_device *dev, unsigned int max)#endif{ struct mv64340_private *mp = netdev_priv(dev); struct net_device_stats *stats = &mp->stats; unsigned int received_packets = 0; struct sk_buff *skb; struct pkt_info pkt_info;#ifdef MV64340_NAPI while (eth_port_receive(mp, &pkt_info) == ETH_OK && budget > 0) {#else while ((--max) && eth_port_receive(mp, &pkt_info) == ETH_OK) {#endif mp->rx_ring_skbs--; received_packets++;#ifdef MV64340_NAPI budget--;#endif /* Update statistics. Note byte count includes 4 byte CRC count */ stats->rx_packets++; stats->rx_bytes += pkt_info.byte_cnt; skb = (struct sk_buff *) pkt_info.return_info; /* * In case received a packet without first / last bits on OR * the error summary bit is on, the packets needs to be dropeed. */ if (((pkt_info.cmd_sts & (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) != (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) || (pkt_info.cmd_sts & ETH_ERROR_SUMMARY)) { stats->rx_dropped++; if ((pkt_info.cmd_sts & (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) != (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) { if (net_ratelimit()) printk(KERN_ERR "%s: Received packet spread on multiple" " descriptors\n", dev->name); } if (pkt_info.cmd_sts & ETH_ERROR_SUMMARY) stats->rx_errors++; dev_kfree_skb_irq(skb); } else { /* * The -4 is for the CRC in the trailer of the * received packet */ skb_put(skb, pkt_info.byte_cnt - 4); skb->dev = dev;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?