Linux网卡驱动pcnet32.c的注释(AM79C9*系列网卡的驱动)
很久之前读的网卡驱动源码,很多东西已经忘记了,最近面试被问道了网卡收数据包的全过程,只能答出一个很简单的过程,NAPI这种非常优秀的机制都没有想起来,很惭愧,重新复习了一下收包的过程,顺便把当时注释的代码贴出来,仅供参考。注释内容主要关注AM79C970A,为了方便阅读分成了几个代码块。
/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */
/** Copyright 1996-1999 Thomas Bogendoerfer** Derived from the lance driver written 1993,1994,1995 by Donald Becker.*Copyright 1993 United States Government as represented by theDirector, National Security Agency.*This software may be used and distributed according to the termsof the GNU General Public License, incorporated herein by reference.*This driver is for PCnet32 and PCnetPCI based ethercards*/
/*************************************************************************** 23 Oct, 2000.* Fixed a few bugs, related to running the controller in 32bit mode.** Carsten Langgaard, carstenl@mips.com* Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.**************************************************************************/#define DRV_NAME "pcnet32"
#ifdef CONFIG_PCNET32_NAPI
#define DRV_VERSION "1.34-NAPI"
#else
#define DRV_VERSION "1.34"
#endif
#define DRV_RELDATE "14.Aug.2007"
#define PFX DRV_NAME ": "static const char *const version =DRV_NAME".c:v"DRV_VERSION" "DRV_RELDATE" tsbogend@alpha.franken.de\n";#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <linux/bitops.h>#include <asm/dma.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>/** PCI device identifiers for "new style" Linux PCI Device Drivers*/
static struct pci_device_id pcnet32_pci_tbl[] = {{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE_HOME), },{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE), },/** Adapters that were sold with IBM's RS/6000 or pSeries hardware have* the incorrect vendor id.*/{ PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE),.class = (PCI_CLASS_NETWORK_ETHERNET << 8), .class_mask = 0xffff00, },} /* terminate list */
};MODULE_DEVICE_TABLE(pci, pcnet32_pci_tbl);static int cards_found;/** VLB I/O addresses*/
static unsigned int pcnet32_portlist[] __initdata ={ 0x300, 0x320, 0x340, 0x360, 0 };static int pcnet32_debug = 0;
static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
static int pcnet32vlb; /* check for VLB cards ? */static struct net_device *pcnet32_dev;static int max_interrupt_work = 2;
static int rx_copybreak = 200;#define PCNET32_PORT_AUI 0x00
#define PCNET32_PORT_10BT 0x01
#define PCNET32_PORT_GPSI 0x02
#define PCNET32_PORT_MII 0x03#define PCNET32_PORT_PORTSEL 0x03
#define PCNET32_PORT_ASEL 0x04
#define PCNET32_PORT_100 0x40
#define PCNET32_PORT_FD 0x80#define PCNET32_DMA_MASK 0xffffffff#define PCNET32_WATCHDOG_TIMEOUT (jiffies + (2 * HZ))
#define PCNET32_BLINK_TIMEOUT (jiffies + (HZ/4))/** table to translate option values from tulip* to internal options*/
static const unsigned char options_mapping[] = {PCNET32_PORT_ASEL, /* 0 Auto-select */PCNET32_PORT_AUI, /* 1 BNC/AUI */PCNET32_PORT_AUI, /* 2 AUI/BNC */PCNET32_PORT_ASEL, /* 3 not supported */PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */PCNET32_PORT_ASEL, /* 5 not supported */PCNET32_PORT_ASEL, /* 6 not supported */PCNET32_PORT_ASEL, /* 7 not supported */PCNET32_PORT_ASEL, /* 8 not supported */PCNET32_PORT_MII, /* 9 MII 10baseT */PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */PCNET32_PORT_MII, /* 11 MII (autosel) */PCNET32_PORT_10BT, /* 12 10BaseT */PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx *//* 14 MII 100BaseTx-FD */PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD,PCNET32_PORT_ASEL /* 15 not supported */
};static const char pcnet32_gstrings_test[][ETH_GSTRING_LEN] = {"Loopback test (offline)"
};#define PCNET32_TEST_LEN ARRAY_SIZE(pcnet32_gstrings_test)#define PCNET32_NUM_REGS 136#define MAX_UNITS 8 /* More are supported, limit only on options */
static int options[MAX_UNITS];
static int full_duplex[MAX_UNITS];
static int homepna[MAX_UNITS];/** Theory of Operation** This driver uses the same software structure as the normal lance* driver. So look for a verbose description in lance.c. The differences* to the normal lance driver is the use of the 32bit mode of PCnet32* and PCnetPCI chips. Because these chips are 32bit chips, there is no* 16MB limitation and we don't need bounce buffers.*//** Set the number of Tx and Rx buffers, using Log_2(# buffers).* Reasonable default values are 4 Tx buffers, and 16 Rx buffers.* That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).*/
#ifndef PCNET32_LOG_TX_BUFFERS
#define PCNET32_LOG_TX_BUFFERS 4
#define PCNET32_LOG_RX_BUFFERS 5
#define PCNET32_LOG_MAX_TX_BUFFERS 9 /* 2^9 == 512 */
#define PCNET32_LOG_MAX_RX_BUFFERS 9
#endif#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS))
#define TX_MAX_RING_SIZE (1 << (PCNET32_LOG_MAX_TX_BUFFERS))#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS))
#define RX_MAX_RING_SIZE (1 << (PCNET32_LOG_MAX_RX_BUFFERS))#define PKT_BUF_SKB 1544
/* actual buffer length after being aligned */
#define PKT_BUF_SIZE (PKT_BUF_SKB - NET_IP_ALIGN)
/* chip wants twos complement of the (aligned) buffer length */
#define NEG_BUF_SIZE (NET_IP_ALIGN - PKT_BUF_SKB)/* Offsets from base I/O address. */
#define PCNET32_WIO_RDP 0x10
#define PCNET32_WIO_RAP 0x12
#define PCNET32_WIO_RESET 0x14
#define PCNET32_WIO_BDP 0x16#define PCNET32_DWIO_RDP 0x10
#define PCNET32_DWIO_RAP 0x14
#define PCNET32_DWIO_RESET 0x18
#define PCNET32_DWIO_BDP 0x1C#define PCNET32_TOTAL_SIZE 0x20#define CSR0 0
#define CSR0_INIT 0x1
#define CSR0_START 0x2
#define CSR0_STOP 0x4
#define CSR0_TXPOLL 0x8
#define CSR0_INTEN 0x40
#define CSR0_IDON 0x0100
#define CSR0_NORMAL (CSR0_START | CSR0_INTEN)
#define PCNET32_INIT_LOW 1
#define PCNET32_INIT_HIGH 2
#define CSR3 3
#define CSR4 4
#define CSR5 5
#define CSR5_SUSPEND 0x0001
#define CSR15 15
#define PCNET32_MC_FILTER 8#define PCNET32_79C970A 0x2621/* The PCNET32 Rx and Tx ring descriptors. */
struct pcnet32_rx_head {__le32 base;///存储该描述符对应的缓冲区的首地址__le16 buf_length; /* two`s complement of length */ ///二进制补码形式,缓冲区大小__le16 status;每一位都有自己的含义,硬件手册有规定__le32 msg_length;__le32 reserved;
};struct pcnet32_tx_head {__le32 base;__le16 length; /* two`s complement of length */__le16 status;__le32 misc; ///用于错误标示和计数,详见硬件手册__le32 reserved;
};//* The PCNET32 32-Bit initialization block, described in databook. */
/**The Mode Register (CSR15) allows alteration of the chip’s operating *parameters. The Mode field of the Initialization Block is copied directly *into CSR15. Normal operation is the result of configuring the Mode field *with all bits zero.*/
struct pcnet32_init_block {__le16 mode;__le16 tlen_rlen;u8 phys_addr[6];__le16 reserved;__le32 filter[2];/* Receive and transmit ring base, along with extra bits. */__le32 rx_ring;__le32 tx_ring;
};
/* PCnet32 access functions */
struct pcnet32_access {u16 (*read_csr) (unsigned long, int);void (*write_csr) (unsigned long, int, u16);u16 (*read_bcr) (unsigned long, int);void (*write_bcr) (unsigned long, int, u16);u16 (*read_rap) (unsigned long);void (*write_rap) (unsigned long, u16);void (*reset) (unsigned long);
};/** The first field of pcnet32_private is read by the ethernet device* so the structure should be allocated using pci_alloc_consistent().*/
struct pcnet32_private {struct pcnet32_init_block *init_block;/* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */struct pcnet32_rx_head *rx_ring;struct pcnet32_tx_head *tx_ring;dma_addr_t init_dma_addr;/* DMA address of beginning of the init block,returned by pci_alloc_consistent */struct pci_dev *pci_dev;const char *name;/* The saved address of a sent-in-place packet/buffer, for skfree(). */struct sk_buff **tx_skbuff;struct sk_buff **rx_skbuff;dma_addr_t *tx_dma_addr;dma_addr_t *rx_dma_addr;struct pcnet32_access a;spinlock_t lock; /* Guard lock */unsigned int cur_rx, cur_tx; /* The next free ring entry */unsigned int rx_ring_size; /* current rx ring size */unsigned int tx_ring_size; /* current tx ring size */unsigned int rx_mod_mask; /* rx ring modular mask */unsigned int tx_mod_mask; /* tx ring modular mask */unsigned short rx_len_bits;unsigned short tx_len_bits;dma_addr_t rx_ring_dma_addr;dma_addr_t tx_ring_dma_addr;unsigned int dirty_rx, /* ring entries to be freed. */dirty_tx;struct net_device *dev;struct napi_struct napi;char tx_full;char phycount; /* number of phys found */int options;///shared_irq为1时表示irq是共享的(allow sharing the irq among several devices),为0表示不共享unsigned int shared_irq:1, /* shared irq possible */dxsuflo:1, /* disable transmit stop on uflo */mii:1; /* mii port available */struct net_device *next;struct mii_if_info mii_if;struct timer_list watchdog_timer;struct timer_list blink_timer;u32 msg_enable; /* debug message level *//* each bit indicates an available PHY */u32 phymask;unsigned short chip_version; /* which variant this is */
};static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
static int pcnet32_probe1(unsigned long, int, struct pci_dev *);
static int pcnet32_open(struct net_device *);
static int pcnet32_init_ring(struct net_device *);
static int pcnet32_start_xmit(struct sk_buff *, struct net_device *);
static void pcnet32_tx_timeout(struct net_device *dev);
static irqreturn_t pcnet32_interrupt(int, void *);
static int pcnet32_close(struct net_device *);
static struct net_device_stats *pcnet32_get_stats(struct net_device *);
static void pcnet32_load_multicast(struct net_device *dev);
static void pcnet32_set_multicast_list(struct net_device *);
static int pcnet32_ioctl(struct net_device *, struct ifreq *, int);
static void pcnet32_watchdog(struct net_device *);
static int mdio_read(struct net_device *dev, int phy_id, int reg_num);
static void mdio_write(struct net_device *dev, int phy_id, int reg_num,int val);
static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits);
static void pcnet32_ethtool_test(struct net_device *dev,struct ethtool_test *eth_test, u64 * data);
static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1);
static int pcnet32_phys_id(struct net_device *dev, u32 data);
static void pcnet32_led_blink_callback(struct net_device *dev);
static int pcnet32_get_regs_len(struct net_device *dev);
static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,void *ptr);
static void pcnet32_purge_tx_ring(struct net_device *dev);
static int pcnet32_alloc_ring(struct net_device *dev, char *name);
static void pcnet32_free_ring(struct net_device *dev);
static void pcnet32_check_media(struct net_device *dev, int verbose);
/*
工作在16-bit IO mode下,要读取第index个CSR的值,首先往RAP中写入index,然后再从RDP
中读取数据(此时读到的数据就是第index个CSR低16bit的数据),
*/
static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
{outw(index, addr + PCNET32_WIO_RAP);return inw(addr + PCNET32_WIO_RDP);
}/*工作在16-bit IO mode下,要往第index个CSR中写入数据,首先往RAP中写入index,然后
再往RDP中写入数据(数据会自动被写入CSR index),写入数据超过16bit的话,高位会被忽略掉*/
static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val)
{outw(index, addr + PCNET32_WIO_RAP);outw(val, addr + PCNET32_WIO_RDP);
}/*
与static u16 pcnet32_wio_read_csr(unsigned long addr, int index)访问方
式类似,唯一不同的是读取数据是从BDP中读取
*/
static u16 pcnet32_wio_read_bcr(unsigned long addr, int index)
{outw(index, addr + PCNET32_WIO_RAP);return inw(addr + PCNET32_WIO_BDP);
}
/*
与static void pcnet32_wio_write_csr(unsigned long addr, int index, u16
val)访问方式类似,唯一不同的是读取数据是从BDP中读取
*/
static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val)
{outw(index, addr + PCNET32_WIO_RAP);outw(val, addr + PCNET32_WIO_BDP);
}static u16 pcnet32_wio_read_rap(unsigned long addr)
{return inw(addr + PCNET32_WIO_RAP);
}static void pcnet32_wio_write_rap(unsigned long addr, u16 val)
{outw(val, addr + PCNET32_WIO_RAP);
}///Reset causes the device to cease operation and clear its internal logic.
static void pcnet32_wio_reset(unsigned long addr)
{///read access to the RESET address (i.e., offset 0x14 for 16-bit I/O, offset 0x18 for 32-bit I/O),inw(addr + PCNET32_WIO_RESET); /// addr + 0x14
}static int pcnet32_wio_check(unsigned long addr)
{outw(88, addr + PCNET32_WIO_RAP);return (inw(addr + PCNET32_WIO_RAP) == 88);
}static struct pcnet32_access pcnet32_wio = {.read_csr = pcnet32_wio_read_csr,.write_csr = pcnet32_wio_write_csr,.read_bcr = pcnet32_wio_read_bcr,.write_bcr = pcnet32_wio_write_bcr,.read_rap = pcnet32_wio_read_rap,.write_rap = pcnet32_wio_write_rap,.reset = pcnet32_wio_reset
};static u16 pcnet32_dwio_read_csr(unsigned long addr, int index)
{outl(index, addr + PCNET32_DWIO_RAP);return (inl(addr + PCNET32_DWIO_RDP) & 0xffff);
}static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val)
{outl(index, addr + PCNET32_DWIO_RAP);outl(val, addr + PCNET32_DWIO_RDP);
}static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index)
{outl(index, addr + PCNET32_DWIO_RAP);return (inl(addr + PCNET32_DWIO_BDP) & 0xffff);
}static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val)
{outl(index, addr + PCNET32_DWIO_RAP);outl(val, addr + PCNET32_DWIO_BDP);
}static u16 pcnet32_dwio_read_rap(unsigned long addr)
{return (inl(addr + PCNET32_DWIO_RAP) & 0xffff);
}static void pcnet32_dwio_write_rap(unsigned long addr, u16 val)
{outl(val, addr + PCNET32_DWIO_RAP);
}static void pcnet32_dwio_reset(unsigned long addr)
{inl(addr + PCNET32_DWIO_RESET);
}static int pcnet32_dwio_check(unsigned long addr)
{outl(88, addr + PCNET32_DWIO_RAP);return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88);
}static struct pcnet32_access pcnet32_dwio = {.read_csr = pcnet32_dwio_read_csr,.write_csr = pcnet32_dwio_write_csr,.read_bcr = pcnet32_dwio_read_bcr,.write_bcr = pcnet32_dwio_write_bcr,.read_rap = pcnet32_dwio_read_rap,.write_rap = pcnet32_dwio_write_rap,.reset = pcnet32_dwio_reset
};
static void pcnet32_netif_stop(struct net_device *dev)
{
#ifdef CONFIG_PCNET32_NAPIstruct pcnet32_private *lp = netdev_priv(dev);
#endifdev->trans_start = jiffies;
#ifdef CONFIG_PCNET32_NAPInapi_disable(&lp->napi);
#endifnetif_tx_disable(dev);
}static void pcnet32_netif_start(struct net_device *dev)
{
#ifdef CONFIG_PCNET32_NAPIstruct pcnet32_private *lp = netdev_priv(dev);ulong ioaddr = dev->base_addr;u16 val;
#endifnetif_wake_queue(dev);
#ifdef CONFIG_PCNET32_NAPIval = lp->a.read_csr(ioaddr, CSR3);val &= 0x00ff;lp->a.write_csr(ioaddr, CSR3, val);napi_enable(&lp->napi);
#endif
}/** Allocate space for the new sized tx ring.* Free old resources* Save new resources.* Any failure keeps old resources.* Must be called with lp->lock held.*/
static void pcnet32_realloc_tx_ring(struct net_device *dev,struct pcnet32_private *lp,unsigned int size)
{dma_addr_t new_ring_dma_addr;dma_addr_t *new_dma_addr_list;struct pcnet32_tx_head *new_tx_ring;struct sk_buff **new_skb_list;pcnet32_purge_tx_ring(dev);new_tx_ring = pci_alloc_consistent(lp->pci_dev,sizeof(struct pcnet32_tx_head) *(1 << size),&new_ring_dma_addr);if (new_tx_ring == NULL) {if (netif_msg_drv(lp))printk("\n" KERN_ERR"%s: Consistent memory allocation failed.\n",dev->name);return;}memset(new_tx_ring, 0, sizeof(struct pcnet32_tx_head) * (1 << size));new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t),GFP_ATOMIC);if (!new_dma_addr_list) {if (netif_msg_drv(lp))printk("\n" KERN_ERR"%s: Memory allocation failed.\n", dev->name);goto free_new_tx_ring;}new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *),GFP_ATOMIC);if (!new_skb_list) {if (netif_msg_drv(lp))printk("\n" KERN_ERR"%s: Memory allocation failed.\n", dev->name);goto free_new_lists;}kfree(lp->tx_skbuff);kfree(lp->tx_dma_addr);pci_free_consistent(lp->pci_dev,sizeof(struct pcnet32_tx_head) *lp->tx_ring_size, lp->tx_ring,lp->tx_ring_dma_addr);lp->tx_ring_size = (1 << size);lp->tx_mod_mask = lp->tx_ring_size - 1;lp->tx_len_bits = (size << 12);lp->tx_ring = new_tx_ring;lp->tx_ring_dma_addr = new_ring_dma_addr;lp->tx_dma_addr = new_dma_addr_list;lp->tx_skbuff = new_skb_list;return;free_new_lists:kfree(new_dma_addr_list);free_new_tx_ring:pci_free_consistent(lp->pci_dev,sizeof(struct pcnet32_tx_head) *(1 << size),new_tx_ring,new_ring_dma_addr);return;
}/** Allocate space for the new sized rx ring.* Re-use old receive buffers.* alloc extra buffers* free unneeded buffers* free unneeded buffers* Save new resources.* Any failure keeps old resources.* Must be called with lp->lock held.*/
static void pcnet32_realloc_rx_ring(struct net_device *dev,struct pcnet32_private *lp,unsigned int size)
{dma_addr_t new_ring_dma_addr;dma_addr_t *new_dma_addr_list;struct pcnet32_rx_head *new_rx_ring;struct sk_buff **new_skb_list;int new, overlap;new_rx_ring = pci_alloc_consistent(lp->pci_dev,sizeof(struct pcnet32_rx_head) *(1 << size),&new_ring_dma_addr);if (new_rx_ring == NULL) {if (netif_msg_drv(lp))printk("\n" KERN_ERR"%s: Consistent memory allocation failed.\n",dev->name);return;}memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * (1 << size));new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t),GFP_ATOMIC);if (!new_dma_addr_list) {if (netif_msg_drv(lp))printk("\n" KERN_ERR"%s: Memory allocation failed.\n", dev->name);goto free_new_rx_ring;}new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *),GFP_ATOMIC);if (!new_skb_list) {if (netif_msg_drv(lp))printk("\n" KERN_ERR"%s: Memory allocation failed.\n", dev->name);goto free_new_lists;}/* first copy the current receive buffers */overlap = min(size, lp->rx_ring_size);for (new = 0; new < overlap; new++) {new_rx_ring[new] = lp->rx_ring[new];new_dma_addr_list[new] = lp->rx_dma_addr[new];new_skb_list[new] = lp->rx_skbuff[new];}/* now allocate any new buffers needed */for (; new < size; new++ ) {struct sk_buff *rx_skbuff;new_skb_list[new] = dev_alloc_skb(PKT_BUF_SKB);if (!(rx_skbuff = new_skb_list[new])) {/* keep the original lists and buffers */if (netif_msg_drv(lp))printk(KERN_ERR"%s: pcnet32_realloc_rx_ring dev_alloc_skb failed.\n",dev->name);goto free_all_new;}skb_reserve(rx_skbuff, NET_IP_ALIGN);new_dma_addr_list[new] =pci_map_single(lp->pci_dev, rx_skbuff->data,PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);new_rx_ring[new].base = cpu_to_le32(new_dma_addr_list[new]);new_rx_ring[new].buf_length = cpu_to_le16(NEG_BUF_SIZE);new_rx_ring[new].status = cpu_to_le16(0x8000);}/* and free any unneeded buffers */for (; new < lp->rx_ring_size; new++) {if (lp->rx_skbuff[new]) {pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[new],PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);dev_kfree_skb(lp->rx_skbuff[new]);}}kfree(lp->rx_skbuff);kfree(lp->rx_dma_addr);pci_free_consistent(lp->pci_dev,sizeof(struct pcnet32_rx_head) *lp->rx_ring_size, lp->rx_ring,lp->rx_ring_dma_addr);lp->rx_ring_size = (1 << size);lp->rx_mod_mask = lp->rx_ring_size - 1;lp->rx_len_bits = (size << 4);lp->rx_ring = new_rx_ring;lp->rx_ring_dma_addr = new_ring_dma_addr;lp->rx_dma_addr = new_dma_addr_list;lp->rx_skbuff = new_skb_list;return;free_all_new:for (; --new >= lp->rx_ring_size; ) {if (new_skb_list[new]) {pci_unmap_single(lp->pci_dev, new_dma_addr_list[new],PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);dev_kfree_skb(new_skb_list[new]);}}kfree(new_skb_list);free_new_lists:kfree(new_dma_addr_list);free_new_rx_ring:pci_free_consistent(lp->pci_dev,sizeof(struct pcnet32_rx_head) *(1 << size),new_rx_ring,new_ring_dma_addr);return;
}
////释放分配给lp->rx_skbuff的DMA缓冲区,是发生错误后的处理函数
static void pcnet32_purge_rx_ring(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);int i;/* free all allocated skbuffs */for (i = 0; i < lp->rx_ring_size; i++) {lp->rx_ring[i].status = 0; /* CPU owns buffer */wmb(); /* Make sure adapter sees owner change */if (lp->rx_skbuff[i]) {pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);dev_kfree_skb_any(lp->rx_skbuff[i]);}lp->rx_skbuff[i] = NULL;lp->rx_dma_addr[i] = 0;}
}#ifdef CONFIG_NET_POLL_CONTROLLER
/** 第一步:关中断,我们平时说的关CPU的中断,指的应该是全关,而不是仅关掉一个吧* 第二步:进行中断处理(这个不是中断服务程序吧??* 第三部:中断处理结束后,开中断*/
static void pcnet32_poll_controller(struct net_device *dev)
{disable_irq(dev->irq);pcnet32_interrupt(0, dev);enable_irq(dev->irq);
}
#endifstatic int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long flags;int r = -EOPNOTSUPP;if (lp->mii) {spin_lock_irqsave(&lp->lock, flags);mii_ethtool_gset(&lp->mii_if, cmd);spin_unlock_irqrestore(&lp->lock, flags);r = 0;}return r;
}static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long flags;int r = -EOPNOTSUPP;if (lp->mii) {spin_lock_irqsave(&lp->lock, flags);r = mii_ethtool_sset(&lp->mii_if, cmd);spin_unlock_irqrestore(&lp->lock, flags);}return r;
}///读取驱动的名字,版本,pci设备的名字(对于PCI总线即pdev->dev.bus_id),并放入ethtool_drvinfo结构体中
static void pcnet32_get_drvinfo(struct net_device *dev,struct ethtool_drvinfo *info)
{struct pcnet32_private *lp = netdev_priv(dev);strcpy(info->driver, DRV_NAME);strcpy(info->version, DRV_VERSION);if (lp->pci_dev)strcpy(info->bus_info, pci_name(lp->pci_dev));elsesprintf(info->bus_info, "VLB 0x%lx", dev->base_addr);
}///判断当前链接状态,正常返回0,异常返回1
static u32 pcnet32_get_link(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long flags;int r;spin_lock_irqsave(&lp->lock, flags);if (lp->mii) {r = mii_link_ok(&lp->mii_if);} else if (lp->chip_version >= PCNET32_79C970A) {ulong ioaddr = dev->base_addr; /* card base I/O address */r = (lp->a.read_bcr(ioaddr, 4) != 0xc0); ////0xc0 = 1100 0000} else { /* can not detect link on really old chips */r = 1;}spin_unlock_irqrestore(&lp->lock, flags);return r;
}////返回dev的msg_enable(即debug level)
static u32 pcnet32_get_msglevel(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);return lp->msg_enable;
}///把dev的msg_enable(即debug level)设置为value
static void pcnet32_set_msglevel(struct net_device *dev, u32 value)
{struct pcnet32_private *lp = netdev_priv(dev);lp->msg_enable = value;
}
////因为VMware提供的虚拟网卡是AM79C970A,所以lp->mii=0,函数什么都没做,直接返回 -EOPNOTSUPP
static int pcnet32_nway_reset(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long flags;int r = -EOPNOTSUPP;if (lp->mii) {spin_lock_irqsave(&lp->lock, flags);r = mii_nway_restart(&lp->mii_if);spin_unlock_irqrestore(&lp->lock, flags);}return r;
}////把最大rx ring 和 tx ring大小的最大值和当前值
static void pcnet32_get_ringparam(struct net_device *dev,struct ethtool_ringparam *ering)
{struct pcnet32_private *lp = netdev_priv(dev);ering->tx_max_pending = TX_MAX_RING_SIZE;ering->tx_pending = lp->tx_ring_size;ering->rx_max_pending = RX_MAX_RING_SIZE;ering->rx_pending = lp->rx_ring_size;
}static int pcnet32_set_ringparam(struct net_device *dev,struct ethtool_ringparam *ering)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long flags;unsigned int size;ulong ioaddr = dev->base_addr;int i;if (ering->rx_mini_pending || ering->rx_jumbo_pending)return -EINVAL;if (netif_running(dev))pcnet32_netif_stop(dev);spin_lock_irqsave(&lp->lock, flags);lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */size = min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);/* set the minimum ring size to 4, to allow the loopback test to work* unchanged.*/for (i = 2; i <= PCNET32_LOG_MAX_TX_BUFFERS; i++) {if (size <= (1 << i))break;}if ((1 << i) != lp->tx_ring_size)pcnet32_realloc_tx_ring(dev, lp, i);size = min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE);for (i = 2; i <= PCNET32_LOG_MAX_RX_BUFFERS; i++) {if (size <= (1 << i))break;}if ((1 << i) != lp->rx_ring_size)pcnet32_realloc_rx_ring(dev, lp, i);lp->napi.weight = lp->rx_ring_size / 2;if (netif_running(dev)) {pcnet32_netif_start(dev);pcnet32_restart(dev, CSR0_NORMAL);}spin_unlock_irqrestore(&lp->lock, flags);if (netif_msg_drv(lp))printk(KERN_INFO"%s: Ring Param Settings: RX: %d, TX: %d\n", dev->name,lp->rx_ring_size, lp->tx_ring_size);return 0;
}static void pcnet32_get_strings(struct net_device *dev, u32 stringset,u8 * data)
{memcpy(data, pcnet32_gstrings_test, sizeof(pcnet32_gstrings_test));
}static int pcnet32_get_sset_count(struct net_device *dev, int sset)
{switch (sset) {case ETH_SS_TEST:return PCNET32_TEST_LEN;default:return -EOPNOTSUPP;}
}static void pcnet32_ethtool_test(struct net_device *dev,struct ethtool_test *test, u64 * data)
{struct pcnet32_private *lp = netdev_priv(dev);int rc;if (test->flags == ETH_TEST_FL_OFFLINE) {rc = pcnet32_loopback_test(dev, data);if (rc) {if (netif_msg_hw(lp))printk(KERN_DEBUG "%s: Loopback test failed.\n",dev->name);test->flags |= ETH_TEST_FL_FAILED;} else if (netif_msg_hw(lp))printk(KERN_DEBUG "%s: Loopback test passed.\n",dev->name);} else if (netif_msg_hw(lp))printk(KERN_DEBUG"%s: No tests to run (specify 'Offline' on ethtool).",dev->name);
} /* end pcnet32_ethtool_test */static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
{struct pcnet32_private *lp = netdev_priv(dev);struct pcnet32_access *a = &lp->a; /* access to registers */ulong ioaddr = dev->base_addr; /* card base I/O address */struct sk_buff *skb; /* sk buff */int x, i; /* counters */int numbuffs = 4; /* number of TX/RX buffers and descs */u16 status = 0x8300; /* TX ring status */__le16 teststatus; /* test of ring status */int rc; /* return code */int size; /* size of packets */unsigned char *packet; /* source packet data */static const int data_len = 60; /* length of source packets */unsigned long flags;unsigned long ticks;rc = 1; /* default to fail */if (netif_running(dev))
#ifdef CONFIG_PCNET32_NAPIpcnet32_netif_stop(dev);
#elsepcnet32_close(dev);
#endifspin_lock_irqsave(&lp->lock, flags);lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size));/* Reset the PCNET32 */lp->a.reset(ioaddr);lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad *//* switch pcnet32 to 32bit mode */lp->a.write_bcr(ioaddr, 20, 2);/* purge & init rings but don't actually restart */pcnet32_restart(dev, 0x0000);lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit *//* Initialize Transmit buffers. */size = data_len + 15;for (x = 0; x < numbuffs; x++) {if (!(skb = dev_alloc_skb(size))) {if (netif_msg_hw(lp))printk(KERN_DEBUG"%s: Cannot allocate skb at line: %d!\n",dev->name, __LINE__);goto clean_up;} else {packet = skb->data;skb_put(skb, size); /* create space for data */lp->tx_skbuff[x] = skb;lp->tx_ring[x].length = cpu_to_le16(-skb->len);lp->tx_ring[x].misc = 0;/* put DA and SA into the skb */for (i = 0; i < 6; i++)*packet++ = dev->dev_addr[i];for (i = 0; i < 6; i++)*packet++ = dev->dev_addr[i];/* type */*packet++ = 0x08;*packet++ = 0x06;/* packet number */*packet++ = x;/* fill packet with data */for (i = 0; i < data_len; i++)*packet++ = i;lp->tx_dma_addr[x] =pci_map_single(lp->pci_dev, skb->data, skb->len,PCI_DMA_TODEVICE);lp->tx_ring[x].base = cpu_to_le32(lp->tx_dma_addr[x]);wmb(); /* Make sure owner changes after all others are visible */lp->tx_ring[x].status = cpu_to_le16(status);}}x = a->read_bcr(ioaddr, 32); /* set internal loopback in BCR32 */a->write_bcr(ioaddr, 32, x | 0x0002);/* set int loopback in CSR15 */x = a->read_csr(ioaddr, CSR15) & 0xfffc;lp->a.write_csr(ioaddr, CSR15, x | 0x0044);teststatus = cpu_to_le16(0x8000);lp->a.write_csr(ioaddr, CSR0, CSR0_START); /* Set STRT bit *//* Check status of descriptors */for (x = 0; x < numbuffs; x++) {ticks = 0;rmb();while ((lp->rx_ring[x].status & teststatus) && (ticks < 200)) {spin_unlock_irqrestore(&lp->lock, flags);msleep(1);spin_lock_irqsave(&lp->lock, flags);rmb();ticks++;}if (ticks == 200) {if (netif_msg_hw(lp))printk("%s: Desc %d failed to reset!\n",dev->name, x);break;}}lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */wmb();if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) {printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name);for (x = 0; x < numbuffs; x++) {printk(KERN_DEBUG "%s: Packet %d:\n", dev->name, x);skb = lp->rx_skbuff[x];for (i = 0; i < size; i++) {printk("%02x ", *(skb->data + i));}printk("\n");}}x = 0;rc = 0;while (x < numbuffs && !rc) {skb = lp->rx_skbuff[x];packet = lp->tx_skbuff[x]->data;for (i = 0; i < size; i++) {if (*(skb->data + i) != packet[i]) {if (netif_msg_hw(lp))printk(KERN_DEBUG"%s: Error in compare! %2x - %02x %02x\n",dev->name, i, *(skb->data + i),packet[i]);rc = 1;break;}}x++;}clean_up:*data1 = rc;pcnet32_purge_tx_ring(dev);x = a->read_csr(ioaddr, CSR15);a->write_csr(ioaddr, CSR15, (x & ~0x0044)); /* reset bits 6 and 2 */x = a->read_bcr(ioaddr, 32); /* reset internal loopback */a->write_bcr(ioaddr, 32, (x & ~0x0002));#ifdef CONFIG_PCNET32_NAPIif (netif_running(dev)) {pcnet32_netif_start(dev);pcnet32_restart(dev, CSR0_NORMAL);} else {pcnet32_purge_rx_ring(dev);lp->a.write_bcr(ioaddr, 20, 4); /* return to 16bit mode */}spin_unlock_irqrestore(&lp->lock, flags);
#elseif (netif_running(dev)) {spin_unlock_irqrestore(&lp->lock, flags);pcnet32_open(dev);} else {pcnet32_purge_rx_ring(dev);lp->a.write_bcr(ioaddr, 20, 4); /* return to 16bit mode */spin_unlock_irqrestore(&lp->lock, flags);}
#endifreturn (rc);
} /* end pcnet32_loopback_test */
static void pcnet32_led_blink_callback(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);struct pcnet32_access *a = &lp->a;ulong ioaddr = dev->base_addr;unsigned long flags;int i;spin_lock_irqsave(&lp->lock, flags);for (i = 4; i < 8; i++) {a->write_bcr(ioaddr, i, a->read_bcr(ioaddr, i) ^ 0x4000);}spin_unlock_irqrestore(&lp->lock, flags);mod_timer(&lp->blink_timer, PCNET32_BLINK_TIMEOUT);
}static int pcnet32_phys_id(struct net_device *dev, u32 data)
{struct pcnet32_private *lp = netdev_priv(dev);struct pcnet32_access *a = &lp->a;ulong ioaddr = dev->base_addr;unsigned long flags;int i, regs[4];if (!lp->blink_timer.function) {init_timer(&lp->blink_timer);lp->blink_timer.function = (void *)pcnet32_led_blink_callback;lp->blink_timer.data = (unsigned long)dev;}/* Save the current value of the bcrs */spin_lock_irqsave(&lp->lock, flags);for (i = 4; i < 8; i++) {regs[i - 4] = a->read_bcr(ioaddr, i);}spin_unlock_irqrestore(&lp->lock, flags);mod_timer(&lp->blink_timer, jiffies);set_current_state(TASK_INTERRUPTIBLE);/* AV: the limit here makes no sense whatsoever */if ((!data) || (data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ)))data = (u32) (MAX_SCHEDULE_TIMEOUT / HZ);msleep_interruptible(data * 1000);del_timer_sync(&lp->blink_timer);/* Restore the original value of the bcrs */spin_lock_irqsave(&lp->lock, flags);for (i = 4; i < 8; i++) {a->write_bcr(ioaddr, i, regs[i - 4]);}spin_unlock_irqrestore(&lp->lock, flags);return 0;
}/** lp->lock must be held.*/
static int pcnet32_suspend(struct net_device *dev, unsigned long *flags,int can_sleep)
{int csr5;struct pcnet32_private *lp = netdev_priv(dev);struct pcnet32_access *a = &lp->a;ulong ioaddr = dev->base_addr;int ticks;/* really old chips have to be stopped. */if (lp->chip_version < PCNET32_79C970A)return 0;/* set SUSPEND (SPND) - CSR5 bit 0 */csr5 = a->read_csr(ioaddr, CSR5);a->write_csr(ioaddr, CSR5, csr5 | CSR5_SUSPEND);/* poll waiting for bit to be set */ticks = 0;while (!(a->read_csr(ioaddr, CSR5) & CSR5_SUSPEND)) {spin_unlock_irqrestore(&lp->lock, *flags);if (can_sleep)msleep(1);elsemdelay(1);spin_lock_irqsave(&lp->lock, *flags);ticks++;if (ticks > 200) {if (netif_msg_hw(lp))printk(KERN_DEBUG"%s: Error getting into suspend!\n",dev->name);return 0;}}return 1;
}/** process one receive descriptor entry*/
////处理收到的一个descriptor entry,然后交给网络层
static void pcnet32_rx_entry(struct net_device *dev,struct pcnet32_private *lp,struct pcnet32_rx_head *rxp,int entry)
{int status = (short)le16_to_cpu(rxp->status) >> 8; //rxp = &lp->rx_ring[entry]int rx_in_place = 0;struct sk_buff *skb;short pkt_len;//D//他的意思应该是收到的数据包都应该视为独立的frame,不存在buffer chain,如果出现了,说明出错了//这里0x03表示STP位被置,即Start of Packet, ENP被置,即End of Packetif (status != 0x03) { /* There was an error. */ ///** There is a tricky error noted by John Murphy,* <murf@perftech.com> to Russ Nelson: Even with full-sized* buffers it's possible for a jabber packet to use two* buffers, with only the last correctly noting the error.*/// 下面的错误种类和位的对应关系让人有点迷茫,暂时不管if (status & 0x01) /* Only count a general error at the */dev->stats.rx_errors++; /* end of a packet. */if (status & 0x20) //DM//Start of Packetdev->stats.rx_frame_errors++;if (status & 0x10)dev->stats.rx_over_errors++;if (status & 0x08)dev->stats.rx_crc_errors++;if (status & 0x04)dev->stats.rx_fifo_errors++;return;}///prob/ 不太明白这里为何要将数据长度减去4pkt_len = (le32_to_cpu(rxp->msg_length) & 0xfff) - 4;//DM// 帧长大约PKT_BUF_SIZE+4或者小于64字节的包都会被丢掉/* Discard oversize frames. */if (unlikely(pkt_len > PKT_BUF_SIZE)) {if (netif_msg_drv(lp))printk(KERN_ERR "%s: Impossible packet size %d!\n",dev->name, pkt_len);dev->stats.rx_errors++;return;}if (pkt_len < 60) {if (netif_msg_rx_err(lp))printk(KERN_ERR "%s: Runt packet!\n", dev->name);dev->stats.rx_errors++;return;}/**rx_copybreak默认值是200,insmod可传参数指定,推测意思应该是超过rx_copybreak*大小的frame,就进行切断处理*下面的if-else的功能:*如果pkt_len大于copybreak,则取消当前处理的sk_buff的dma映射,并将原来的 lp->*rx_skbuff[entry]指针指向一个新分配的 sk_buff(并使之对其,建立DMA映射),然后*处理原来的sk_buff,并交给网络层,如果不大于,则直接将其数据区拷贝到新的sk_buff,*处理后递交给网路层*/if (pkt_len > rx_copybreak){struct sk_buff *newskb;//// dev_alloc_skb -- allocate an skbuff for sendingif ((newskb = dev_alloc_skb(PKT_BUF_SKB))){/**DM* Increase the headroom of an empty &sk_buff by reducing the tail * room. This is only allowed for an empty buffer.* sk_buff刚分配时只有tail room ,reserve后 前面的NET_IP_ALIGN字节变为 * head room。 是因为共有三个区(两者中间的是packet data),* 四个指针来分界*/skb_reserve(newskb, NET_IP_ALIGN); ////使以太帧头部四字节对其skb = lp->rx_skbuff[entry];pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[entry],PKT_BUF_SIZE,PCI_DMA_FROMDEVICE);///prob/ 使数据区向尾部扩张pkt_len个字节,不太明白为何要这样做skb_put(skb, pkt_len); lp->rx_skbuff[entry] = newskb;lp->rx_dma_addr[entry] =pci_map_single(lp->pci_dev,newskb->data,PKT_BUF_SIZE,PCI_DMA_FROMDEVICE);rxp->base = cpu_to_le32(lp->rx_dma_addr[entry]);rx_in_place = 1;}elseskb = NULL;}else{skb = dev_alloc_skb(pkt_len + NET_IP_ALIGN);}if (skb == NULL) {if (netif_msg_drv(lp))printk(KERN_ERR"%s: Memory squeeze, dropping packet.\n",dev->name);dev->stats.rx_dropped++;return;}skb->dev = dev;if (!rx_in_place) {skb_reserve(skb, NET_IP_ALIGN);skb_put(skb, pkt_len); /* Make room *//*从设备到RAM的一次DMA数据传送完成之前设备驱动程序是不可以访问内存缓冲区的:*相反,如果有必要,在读缓冲区之前,驱动程序应该调用pci_dma_sync_single_for_*cpu()或dma_sync_single_for_cpu()使相应的硬件高速缓存行无效。在80x86体系结*构中,上述函数几乎不做任何事情,因为硬件高速缓存和DMA之间的一致性是由硬件来*维护的。*可以认为下面这句话就是为了维护一致性,*/pci_dma_sync_single_for_cpu(lp->pci_dev,lp->rx_dma_addr[entry],pkt_len,PCI_DMA_FROMDEVICE);///skb_copy_to_linear_data下面函数的实现体只有一句话memcpy(skb->data, from, len);skb_copy_to_linear_data(skb,(unsigned char *)(lp->rx_skbuff[entry]->data),pkt_len);pci_dma_sync_single_for_device(lp->pci_dev,lp->rx_dma_addr[entry],pkt_len,PCI_DMA_FROMDEVICE);}dev->stats.rx_bytes += skb->len;/**(1)将skb->dev指向接收设备*(2)将skb->mac_header指向data(此时data就是指向mac起始地址)*(3)调用skb_pull(skb, ETH_HLEN)将skb->data后移14字节指向ip首部*(4)通过比较目的mac地址判断包的类型,并将skb->pkt_type赋值PACKET_BROADCAST*或PACKET_MULTICAST或者PACKET_OTHERHOST ,因为PACKET_HOST为0,所以是默认值,*HOST表示包的目的地址是本机*(5)最后判断协议类型,并返回(大部分情况下直接返回eth首部的protocol字段的值),*这个返回值被存在skb->protocol字段中*/skb->protocol = eth_type_trans(skb, dev);
#ifdef CONFIG_PCNET32_NAPI/**netif_receive_skb() is the main receive data processing *function,把数据包交给上层协议,任务完成*/netif_receive_skb(skb);
#elsenetif_rx(skb);
#endifdev->last_rx = jiffies;dev->stats.rx_packets++;return;
}/** 完成最多budge个包的接收,然后返回总共接收的包的数量,接收的包均会立刻交给网络层*/
static int pcnet32_rx(struct net_device *dev, int budget)
{struct pcnet32_private *lp = netdev_priv(dev);//cur_rx, cur_tx是The next free ring entry,rx_mod_mask是rx ring modular mask,取余操作int entry = lp->cur_rx & lp->rx_mod_mask;struct pcnet32_rx_head *rxp = &lp->rx_ring[entry];int npackets = 0; //DM//记录已经处理的packet的数量/*由于此时网卡已经将收到的包塞入缓冲区,所以网卡要将own位清0,而own位是status的最*高位 ,所以,此时status的值将>0。*如果OWN位为1(此时status<0),那么表示网卡还没有放数据,此时根据手册,就不能再* 继续读下一个entry了。*//* If we own the next entry, it's a new packet. Send it up. */while (npackets < budget && (short)le16_to_cpu(rxp->status) >= 0) {///处理当前的descriptor entry,并将数据递交给网络层pcnet32_rx_entry(dev, lp, rxp, entry);npackets += 1;/** The docs say that the buffer length isn't touched, but Andrew* Boyd of QNX reports that some revs of the 79C965 clear it.*//** prob/这里不太明白为何长度是个常数 1522 ,可能是因为rx descriptor的 * skb_buff的长度一直都是分配最大值PKT_BUF_SKB 1544,不过1544是错误的, * 因为最大不能超过1518 */rxp->buf_length = cpu_to_le16(NEG_BUF_SIZE);//写内存屏障wmb(); /* Make sure owner changes after others are visible */rxp->status = cpu_to_le16(0x8000); //DM//把rxp的控制权还给controller///处理下一个descriptorentry = (++lp->cur_rx) & lp->rx_mod_mask;rxp = &lp->rx_ring[entry];}return npackets;
}/**** 1.释放已经发送的数据包的缓冲区* 2.检查错误,进行错误处理,如果发生overflow错误,则返回重置网卡信息(must_start=1)* 3.如果tx已经不是满的(也可能是空余的descriptor多余某个数值,后面的逻辑还不是很清晰)* 则置lp->tx_full=0,并通知网络子系统可以重洗启动数据包的发送了*/
static int pcnet32_tx(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned int dirty_tx = lp->dirty_tx;int delta;int must_restart = 0;/**检查错误,如果出现overflow错误,直接返回must_restart,反之释放已经tx_ring中已*经完成发送的descriptor的缓冲区*/while (dirty_tx != lp->cur_tx) {int entry = dirty_tx & lp->tx_mod_mask;int status = (short)le16_to_cpu(lp->tx_ring[entry].status);if (status < 0)break; /* It still hasn't been Txed */lp->tx_ring[entry].base = 0;///ERR位为1,所以出现了错误,检查错误,该累加累加,该打印错误打印错误if (status & 0x4000) {/* There was a major error, log it. */////prob/misc每一位具体干嘛的还不清楚,确定是 标识错误的int err_status = le32_to_cpu(lp->tx_ring[entry].misc); dev->stats.tx_errors++;if (netif_msg_tx_err(lp))printk(KERN_ERR"%s: Tx error status=%04x err_status=%08x\n",dev->name, status,err_status);if (err_status & 0x04000000)dev->stats.tx_aborted_errors++;if (err_status & 0x08000000)dev->stats.tx_carrier_errors++;if (err_status & 0x10000000)dev->stats.tx_window_errors++;///prob/ dxsuflo=0,算not define吗
#ifndef DO_DXSUFLOif (err_status & 0x40000000) {dev->stats.tx_fifo_errors++;/* Ackk! On FIFO errors the Tx unit is turned off! *//* Remove this verbosity later! */if (netif_msg_tx_err(lp))printk(KERN_ERR"%s: Tx FIFO error!\n",dev->name);must_restart = 1;}
#elseif (err_status & 0x40000000) {dev->stats.tx_fifo_errors++;if (!lp->dxsuflo) { /* If controller doesn't recover ... *//* Ackk! On FIFO errors the Tx unit is turned off! *//* Remove this verbosity later! */if (netif_msg_tx_err(lp))printk(KERN_ERR"%s: Tx FIFO error!\n",dev->name);must_restart = 1;}}
#endif} else {///overflow error or CRC errorif (status & 0x1800)dev->stats.collisions++;dev->stats.tx_packets++;}/* We must free the original skb *////释放sk_buff缓冲区(该sk_buff已经发送过了),取消DMA映射if (lp->tx_skbuff[entry]) {pci_unmap_single(lp->pci_dev,lp->tx_dma_addr[entry],lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);dev_kfree_skb_any(lp->tx_skbuff[entry]);lp->tx_skbuff[entry] = NULL;lp->tx_dma_addr[entry] = 0;}dirty_tx++;}///prob/下面的判断逻辑看不懂delta = (lp->cur_tx - dirty_tx) & (lp->tx_mod_mask + lp->tx_ring_size);if (delta > lp->tx_ring_size) {if (netif_msg_drv(lp))printk(KERN_ERR"%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n",dev->name, dirty_tx, lp->cur_tx,lp->tx_full);dirty_tx += lp->tx_ring_size;delta -= lp->tx_ring_size;}///prob/判断如果full不在成立(判断逻辑不明白),则重新置为未满if (lp->tx_full &&netif_queue_stopped(dev) &&delta < lp->tx_ring_size - 2) {/* The ring is no longer full, clear tbusy. */lp->tx_full = 0;netif_wake_queue(dev);}lp->dirty_tx = dirty_tx;return must_restart;
}
////函数体中只有开中断,而没有关中断,所以推测NAPI调用poll之前会自动关闭中断
////完成最多budget个包的接收处理后,返回接收的包的数量
#ifdef CONFIG_PCNET32_NAPI
static int pcnet32_poll(struct napi_struct *napi, int budget)
{/**DM* container_of - cast a member of a structure out to the containing structure* @ptr: the pointer to the member.* @type: the type of the container struct this is embedded in.* @member: the name of the member within the struct.**///DM// 获取lp地址struct pcnet32_private *lp = container_of(napi, struct pcnet32_private, napi);struct net_device *dev = lp->dev;unsigned long ioaddr = dev->base_addr;unsigned long flags;int work_done; //DM// 完成的工作u16 val;/// 接收最多budge个数据包,并递交给网络层,然后返回总共接收的包的数量work_done = pcnet32_rx(dev, budget);spin_lock_irqsave(&lp->lock, flags);//DM//下面像是在做出错处理啊。。。pcnet32_tx还是不知道,判断是否错误是否需要导致重启contorllerif (pcnet32_tx(dev)) {/* reset the chip to clear the error condition, then restart */lp->a.reset(ioaddr);/**DM* 下面不应该有09* CSR4: Test and Features Control*/lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */pcnet32_restart(dev, CSR0_START);netif_wake_queue(dev);}spin_unlock_irqrestore(&lp->lock, flags);//DM// 开中断,关掉中断屏蔽if (work_done < budget) {spin_lock_irqsave(&lp->lock, flags);/* * 实现只有一句话__napi_complete(napi); 功能:Mark NAPI processing as* complete.,推测就是通知NAPI我搞定了*/__netif_rx_complete(dev, napi);/* clear interrupt masks */ //DM// 把CSR3山的中断屏蔽都关掉val = lp->a.read_csr(ioaddr, CSR3);val &= 0x00ff;lp->a.write_csr(ioaddr, CSR3, val);/* Set interrupt enable. */lp->a.write_csr(ioaddr, CSR0, CSR0_INTEN);///也是内存写屏障,不过好像是和PCI的特性有点关系mmiowb();spin_unlock_irqrestore(&lp->lock, flags);}return work_done;
}
#endif#define PCNET32_REGS_PER_PHY 32
#define PCNET32_MAX_PHYS 32
static int pcnet32_get_regs_len(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);int j = lp->phycount * PCNET32_REGS_PER_PHY;return ((PCNET32_NUM_REGS + j) * sizeof(u16));
}static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,void *ptr)
{int i, csr0;u16 *buff = ptr;struct pcnet32_private *lp = netdev_priv(dev);struct pcnet32_access *a = &lp->a;ulong ioaddr = dev->base_addr;unsigned long flags;spin_lock_irqsave(&lp->lock, flags);csr0 = a->read_csr(ioaddr, CSR0);if (!(csr0 & CSR0_STOP)) /* If not stopped */pcnet32_suspend(dev, &flags, 1);/* read address PROM */for (i = 0; i < 16; i += 2)*buff++ = inw(ioaddr + i);/* read control and status registers */for (i = 0; i < 90; i++) {*buff++ = a->read_csr(ioaddr, i);}*buff++ = a->read_csr(ioaddr, 112);*buff++ = a->read_csr(ioaddr, 114);/* read bus configuration registers */for (i = 0; i < 30; i++) {*buff++ = a->read_bcr(ioaddr, i);}*buff++ = 0; /* skip bcr30 so as not to hang 79C976 */for (i = 31; i < 36; i++) {*buff++ = a->read_bcr(ioaddr, i);}/* read mii phy registers */if (lp->mii) {int j;for (j = 0; j < PCNET32_MAX_PHYS; j++) {if (lp->phymask & (1 << j)) {for (i = 0; i < PCNET32_REGS_PER_PHY; i++) {lp->a.write_bcr(ioaddr, 33,(j << 5) | i);*buff++ = lp->a.read_bcr(ioaddr, 34);}}}}if (!(csr0 & CSR0_STOP)) { /* If not stopped */int csr5;/* clear SUSPEND (SPND) - CSR5 bit 0 */csr5 = a->read_csr(ioaddr, CSR5);a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));}spin_unlock_irqrestore(&lp->lock, flags);
}static const struct ethtool_ops pcnet32_ethtool_ops = {.get_settings = pcnet32_get_settings,.set_settings = pcnet32_set_settings,.get_drvinfo = pcnet32_get_drvinfo,strcpy(info->driver, DRV_NAME);strcpy(info->version, DRV_VERSION);if (lp->pci_dev)strcpy(info->bus_info, pci_name(lp->pci_dev));.get_msglevel = pcnet32_get_msglevel,.set_msglevel = pcnet32_set_multicast_listsglevel,.nway_reset = pcnet32_nway_reset,.get_link = pcnet32_get_link,.get_ringparam = pcnet32_get_ringparam,.set_ringparam = pcnet32_set_ringparam,.get_strings = pcnet32_get_strings,.self_test = pcnet32_ethtool_test,.phys_id = pcnet32_phys_id,.get_regs_len = pcnet32_get_regs_len,.get_regs = pcnet32_get_regs,.get_sset_count = pcnet32_get_sset_count,
};/* only probes for non-PCI devices, the rest are handled by* pci_register_driver via pcnet32_probe_pci */static void __devinit pcnet32_probe_vlbus(unsigned int *pcnet32_portlist)
{unsigned int *port, ioaddr;/* search for PCnet32 VLB cards at known addresses */for (port = pcnet32_portlist; (ioaddr = *port); port++) {if (request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) {/* check if there is really a pcnet chip on that ioaddr */if ((inb(ioaddr + 14) == 0x57)&& (inb(ioaddr + 15) == 0x57)) {pcnet32_probe1(ioaddr, 0, NULL);} else {release_region(ioaddr, PCNET32_TOTAL_SIZE);}}}
}///PCI设备启动时,BIOS会自动识别PCI设备,并分配相关资源,pdev是对设备资源的抽象
static int __devinit
pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent)
/** each function can be identified at hardware level by a 16-bit address* the structure pci_dev was used to represent it,so we don't need to act on * the address*/{unsigned long ioaddr;int err;
/*
This function actually enables the device.
It wakes up the device and in some cases also assigns its interrupt line and I/O regions.
*/err = pci_enable_device(pdev);if (err < 0) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_ERR PFX"failed to enable device -- err=%d\n", err);return err;}///为设备pdev启用总线控制,即使得设备具备申请PCI总线控制权的能力( 感觉应该是权限)pci_set_master(pdev);/** PCI配置空间中的6个基地址寄存器,每个都为32位,它们代表PCI的6个IO区域。在linux内核中,* PCI设备的IO区域已被集成到通用资源管理,所以要想获得PCI设备的基地址在存储器域的物理地址,* 要通过下面的函数:* unsigned long pci_resource_start(struct pci_dev *pdev, int bar);* 该函数返回6个PCI IO区域中的第bar个的基地址值(存储器域的物理地址)。bar代表基地址* 寄存器(base address register),取值为0到5.* 所以下面的返回第一个基地址寄存器的值*/ioaddr = pci_resource_start(pdev, 0);if (!ioaddr) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_ERR PFX"card has no PCI IO resources, aborting\n");return -ENODEV;}if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_ERR PFX"architecture does not support 32bit PCI busmaster DMA\n");return -ENODEV;}/**这个函数告诉内核, 你要使用 PCNET32_TOTAL_SIZE 个端口, 从 ioaddr 开始. name *参数应当是你的设备的名子.如果分配成功返回值是非 NULL. *如果你从 request_region 得到 NULL, 你将无法使用需要的端口*/if (request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_pci") ==NULL) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_ERR PFX"io address range already allocated\n");return -EBUSY;}/// 调用pcnet32_probe1()err = pcnet32_probe1(ioaddr, 1, pdev);if (err < 0) {pci_disable_device(pdev);}return err;
}
/* pcnet32_probe1* Called from both pcnet32_probe_vlbus and pcnet_probe_pci.* pdev will be NULL when called from pcnet32_probe_vlbus.*//** 1.获取设别IO基地址,确定IO接口的位数,并赋对应的操作函数结构体* 2.识别芯片版本,设置芯片相关的参数等,比如是否支持full duplex、mii等* 3.给发现的设备分配一个net_device 结构体,并读取硬件中的信息,填充net_device,如 MAC* 4.获取私有数据块地址lp,填充lp的结构,包括分配initialization block、填写irq、mes_enable等* 5.设置napi,并注册poll函数* 6.给net_device分配发送和接收ring* 7.填充initalization block,但是如果pdev非0的话,本函数并不会触发init block写入网卡就寄存器* 8.初始化watchdog,并注册watchdog函数* 9.注册其他操作函数* 10.注册该net_device结构,注意:前面的注册本质只是填充net_device结构体,并非真正的注册* 11.相关出错处理*/
static int __devinit
pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
{struct pcnet32_private *lp;int i, media;int fdx, mii, fset, dxsuflo;int chip_version; ///版本号char *chipname; ///芯片名称struct net_device *dev;struct pcnet32_access *a = NULL;u8 promaddr[6];int ret = -ENODEV;/*79c970A默认的IO mode是16bit 所以这里reset要使用16bit mode的读操作方式*////Reset causes the device to cease operation and clear its internal logic.pcnet32_wio_reset(ioaddr);/****通过往RDP(both 16bit and 32bit are at offset 0x10)中进行写操作来转换成32bit*IO mode指的是contoller的IO寄存器的映射方式*//* NOTE: 16-bit check is first, otherwise some older PCnet chips fail *//*pcnet32_wio_read_csr(ioaddr, 0):读取CSR0的数据,判断是否为4,此时STOP位是置1的,表示此时网卡的状态是inactive(因为pcnet32_wio_reset(ioaddr);已经重置过网卡,网卡此时应为inactive状态///pcnet32_wio_check(ioaddr):向RAP中写88,再从RAP中读取数据,判断是否是88///上面两个操作都是使用16bit的模式(即使用16bit的操作函数集)任意一个条件不满足,都说明网卡不是16位的或者网卡当前的状态不是期望的状态,如果条件都满足,说明是16位的网卡,则将16位的访问函数集赋给变量a(struct pcnet32_access类型)///这是因为,由于16位和32位的RAP偏移量是不一样的,所以在32位的am79c970A上执行16位的访问函数,结果必然错误///如果上述两个操作不全为真,则再尝试以32位模式执行同样的操作,如果结果仍为假,说明网卡状态异常,进入错误处理///经测试,在vmware workstation 7平台上fedora9-32bit上,芯片型号是AM79c970A,默认为16bit mode, pcnet32_wio_reset(ioaddr); 之后往RDP写入任意数据,可以转换至32mode*/if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && pcnet32_wio_check(ioaddr)) {a = &pcnet32_wio;} else {pcnet32_dwio_reset(ioaddr);if (pcnet32_dwio_read_csr(ioaddr, 0) == 4&& pcnet32_dwio_check(ioaddr)) {a = &pcnet32_dwio;} elsegoto err_release_region;}/// read_csr先向RAP中写入(inw) 0,然后读取RDP的值,返回,/// later: the valu e writed into RAP if the CSR number, and the value of the CSR will be/// put into the RDP, so read the RDP after write into the RAP/// read the chip's version number from CSR88 and CSR89chip_version =a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16);///经测试芯片型号是am79c970A,其version number is (0x0242 1003 | 0x0262H << 16) = 0x0262 1003if ((pcnet32_debug & NETIF_MSG_PROBE) && (pcnet32_debug & NETIF_MSG_HW))printk(KERN_INFO " PCnet chip version is %#x.\n",chip_version);/// the last 12 bits of the version number should all be 0x003, I think/// chipversion = 0262 1003Hif ((chip_version & 0xfff) != 0x003) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_INFO PFX "Unsupported chip version.\n");goto err_release_region;}/* initialize variables *//*** fdx表示full duplex 全双工,为1支持全双工,为0表示不能支持全双工* ??????? mii 表示是否支持mii,mii干啥的,* ??????? fset* ??????? dxsuflo未知*/fdx = mii = fset = dxsuflo = 0;/// get the 2 bytes befor 0x003;chip_version = (chip_version >> 12) & 0xffff;/// VMware提供的网卡是79c97cA,chip_version = 2621Hswitch (chip_version) {case 0x2420://// because the chip supplied by vmware is 70c970, so I will concentrate on this casechipname = "PCnet/PCI 79C970"; /* PCI */break;case 0x2430:if (shared)chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */elsechipname = "PCnet/32 79C965"; /* 486/VL bus */break;case 0x2621:chipname = "PCnet/PCI II 79C970A"; /* PCI */fdx = 1;break;case 0x2623:chipname = "PCnet/FAST 79C971"; /* PCI */fdx = 1;mii = 1;fset = 1;break;case 0x2624:chipname = "PCnet/FAST+ 79C972"; /* PCI */fdx = 1;mii = 1;fset = 1;break;case 0x2625:chipname = "PCnet/FAST III 79C973"; /* PCI */fdx = 1;mii = 1;break;case 0x2626:/// well,the 79c978 chip looks like it's a little special, I don't have the hardware manual of it ,and just ignore itchipname = "PCnet/Home 79C978"; /* PCI */fdx = 1;/** This is based on specs published at www.amd.com. This section* assumes that a card with a 79C978 wants to go into standard* ethernet mode. The 79C978 can also go into 1Mb HomePNA mode,* and the module option homepna=1 can select this instead.*/media = a->read_bcr(ioaddr, 49);media &= ~3; /* default to 10Mb ethernet */if (cards_found < MAX_UNITS && homepna[cards_found])media |= 1; /* switch to home wiring mode */if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_DEBUG PFX "media set to %sMbit mode.\n",(media & 1) ? "1" : "10");a->write_bcr(ioaddr, 49, media);break;case 0x2627:chipname = "PCnet/FAST III 79C975"; /* PCI */fdx = 1;mii = 1;break;case 0x2628:chipname = "PCnet/PRO 79C976";fdx = 1;mii = 1;break;default:if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_INFO PFX"PCnet version %#x, no PCnet32 chip.\n",chip_version);goto err_release_region;}/** On selected chips turn on the BCR18:NOUFLO bit. This stops transmit* starting until the packet is loaded. Strike one for reliability, lose* one for latency - although on PCI this isnt a big loss. Ohlder chips* have FIFO's smaller tan a packet, so you can't do this.* Turn on BCR18:BurstRdEn and BCR18:BurstWrEn.*///// case 79c970 ,fset == 0, false, so was ingored by meif (fset) {a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0860));//打开BCR18的第11位,即NOUFLO位,以及第6位(BREADE)和第5位(BWRITE)a->write_csr(ioaddr, 80,(a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);dxsuflo = 1;}/** struct net_device *dev;* 分配一个net_device结构,并分配一个大小为struct * pcnte32_private的size的内存空间即(sizeof(*lp)),并将net_device* 中的priv指向这个大小为sizeof(struct pcnet32_private)的内存空间的的首地址*/dev = alloc_etherdev(sizeof(*lp));/// exception handlingif (!dev) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_ERR PFX "Memory allocation failed.\n");ret = -ENOMEM;goto err_release_region;}/* 把dev->dev.parent设置为pdev->dev。dev->dev 和pdev->dev都是struct * device结构, struct device结构是devcie model中使用的设备节点结构,这句话是为* 了表明net_device对pci_device的继承关系,而pci_device将struct device作为成员* (隐式地表明pci_device是 struct devcie的子类),则是为了将pci_device挂到 * device model设备树的节点上*/SET_NETDEV_DEV(dev, &pdev->dev);if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr);/* In most chips, after a chip reset, the ethernet address is read from the* station address PROM at the base address and programmed into the* "Physical Address Registers" CSR12-14.* As a precautionary measure, we read the PROM values and complain if* they disagree with the CSRs. If they miscompare, and the PROM addr* is valid, then the PROM addr is used.*//*填充dev中的MAC地址。MAC地址存放于CSR12(对应MAC[15:0]),CSR13(MAC[31:16])* ,CSR14(MAC[47:32]),每个CSR均使用低16位* CSR3 bit2可设置网卡使用大端模式或者小端模式,CSR3 bit2为0是为小端,为1时为大端,* 默认值是0,即默认为小端,因为此时网卡已经重置,所以此时网卡使用的是小端,当前CPU* 为x86架构,也为小端,所以不存在大小端转换问题*/for (i = 0; i < 3; i++) {unsigned int val;val = a->read_csr(ioaddr, i + 12) & 0x0ffff;/* There may be endianness issues here. */dev->dev_addr[2 * i] = val & 0x0ff;dev->dev_addr[2 * i + 1] = (val >> 8) & 0x0ff;}/* read PROM address and compare with CSR address *//* *读取AM79C970的IO resourece(起始地址为ioaddr,参见PCnet Family Software Design * Considerations)的byte0到 byte5中存放的48-bit ethernet address,放入promaddr中*/for (i = 0; i < 6; i++)promaddr[i] = inb(ioaddr + i);/* *对从PROM中读取的MAC地址(存放于promaddr)和从CSR12~CSR14中读取的MAC地址(存放于dev->dev_addr)* 进行比较,如果两个值不相等或者dev_addr不是一个有效的MAC地址,则把prom的值拷给dev->dev_addr;/**为什么要比较,因为网卡可能重新打开,那么可能CSR12~CSR14里面的值是之前已经改动过*的MAC地址,非原始MAC地址,所以要把他回复成原始的MAC地址*/if (memcmp(promaddr, dev->dev_addr, 6)|| !is_valid_ether_addr(dev->dev_addr)) {if (is_valid_ether_addr(promaddr)) {if (pcnet32_debug & NETIF_MSG_PROBE) {printk(" warning: CSR address invalid,\n");printk(KERN_INFO" using instead PROM address of");}memcpy(dev->dev_addr, promaddr, 6);}}/////?????网卡MAM地址在哪改,是否通过ioctl()来改/// perm_addr 指的是 Permanent hardware address (即 MAC) netdevice.h中可以找到memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);///??????为什么MAC地址要置0000/* if the ethernet address is not valid, force to 00:00:00:00:00:00 */if (!is_valid_ether_addr(dev->perm_addr))memset(dev->dev_addr, 0, sizeof(dev->dev_addr));if (pcnet32_debug & NETIF_MSG_PROBE) {/// It's a macro which define char mac[MAC_BUF_SIZE] MAC_BUF_SIZE = 18DECLARE_MAC_BUF(mac);printk(" %s", print_mac(mac, dev->dev_addr));/* Version 0x2623 and 0x2624 *//// 79C70A was 0x2621 so ignored,下面没做什么实际工作,就是读取某些寄存器然后输出内容if (((chip_version + 1) & 0xfffe) == 0x2624) {i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */printk("\n" KERN_INFO " tx_start_pt(0x%04x):", i);switch (i >> 10) {case 0:printk(" 20 bytes,");break;case 1:printk(" 64 bytes,");break;case 2:printk(" 128 bytes,");break;case 3:printk("~220 bytes,");break;}i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */printk(" BCR18(%x):", i & 0xffff);if (i & (1 << 5))printk("BurstWrEn ");if (i & (1 << 6))printk("BurstRdEn ");if (i & (1 << 7))printk("DWordIO ");if (i & (1 << 11))printk("NoUFlow ");i = a->read_bcr(ioaddr, 25);//BCR25是SRAM的大小printk("\n" KERN_INFO " SRAMSIZE=0x%04x,", i << 8);i = a->read_bcr(ioaddr, 26);//BCR26: SRAM Boundary Registerprintk(" SRAM_BND=0x%04x,", i << 8);i = a->read_bcr(ioaddr, 27);//BCR27: SRAM Interface Control Registerif (i & (1 << 14))printk("LowLatRx");}}dev->base_addr = ioaddr;///返回net_device *dev 的私有数据地址lp = netdev_priv(dev);/* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment *//////????????一致映射和流式映射的区别if ((lp->init_block =pci_alloc_consistent(pdev, sizeof(*lp->init_block), &lp->init_dma_addr)) == NULL) {//分配initialization blockif (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_ERR PFX"Consistent memory allocation failed.\n");ret = -ENOMEM;goto err_free_netdev;}lp->pci_dev = pdev;lp->dev = dev;spin_lock_init(&lp->lock);SET_NETDEV_DEV(dev, &pdev->dev); 这句话和上面的重复了,删掉lp->name = chipname;/** lp->shared_irq表示是否共享中断号,注册中断服务程序时,lp->shrared_irq * 如果为flags置为IRQ_SHARED,表示共 享中断号,为0则flag置0,表示不共享中断号,共享中* 断号的意思是多个ISR可以注册到同一个IRQ上,这些ISR会被中断子系统以链表的形式连接在* 一起,每一个IRQ都对应这样一个链表,当设备向CPU发出中断请求时,中断子系统会根据IRQ* 遍历对应的链表,链表上的每一个ISR都会被调用一次,(中断子系统给ISR传递两个参数,第* 一个是IRQ,即链表对应的IRQ,第二个是void *dev_id,是可以用 来唯一标示设备的指针,* 这个指针是在注册ISR时传递给中断子系统的,一般都是使用设备对应的结构体的指针。)ISR* 应该先检测自己的硬件有没有产生中断(一般是检测硬件的寄存器的标志位),如果没有则退* 出中断服务程序,此时中断子系统会自动调用链表中的下一个ISR,如果硬件发生中断则开始进* 行中断处理,中断处理完成后ISR返回,中断子系统继续调用链表中下一个ISR*//** 设备向CPU发出中断时,如果CPU此时为开中断,则CPU会立即响应,会立即给中断控制器发* 一个ack消息,中断控制器会将中断号放在数据线上供CPU读取,CPU读取后,会根据中断号找* 到该中断号的中断向量,并且调用该中断向量,注意,当CPU调用中断向量之前,处于关中断状* 态。然后CPU必须在合适的时机(时机一般是调用设备的ISR之前,这是为了提高并发性,即尽* 快让出CPU的中断管脚资源,让其他中断请求可以被受理)向中断控制器发出EOI(end of * interrupt)消息,表示该中断请求已经受理,即告诉中断 控制器不必再发出该中断请求。*/lp->shared_irq = shared;/////lp->tx_ring_size 表示发送ring的大小(即一个ring上有多少个tramsimit descriptor),/// #define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) /// default tx ring size *PCNET32_LOG_TX_BUFFERS是存储在init block中 TLEN域的值 lp->tx_ring_size = TX_RING_SIZE;////和tx_ring_size类似,不同的是rx对应的域是RELNlp->rx_ring_size = RX_RING_SIZE; /* default rx ring size */lp->tx_mod_mask = lp->tx_ring_size - 1; ////绕圈时通过tx_mod_mask取余来获得当前在ring中的位置坐标lp->rx_mod_mask = lp->rx_ring_size - 1;/** 这里的tx_len_bits和rx_len_bits是后面用来填充init block的 tlen_rlen成员的(tlen_rlen为__le16类型的 变量)。 tlen_rlen成员的bit12~big15用来存放tx_len_bits,* bit4~bit7用来存放rx_len_bigs,tlen_rlen = lp->tx_len_bits | lp->rx_len_bits,* 左移是为了把tx_len_bits、rx_len_bits准确地填入tlen_rlen对应位置中*/lp->tx_len_bits = (PCNET32_LOG_TX_BUFFERS << 12); ////(4 << 12)lp->rx_len_bits = (PCNET32_LOG_RX_BUFFERS << 4); (5 << 4)??????/下面三行弄清楚作用lp->mii_if.full_duplex = fdx;lp->mii_if.phy_id_mask = 0x1f;lp->mii_if.reg_num_mask = 0x1f;lp->dxsuflo = dxsuflo; /// for 79c970A dxsuflo = 0;lp->mii = mii; /// for 79c970A mii = 0;lp->chip_version = chip_version;lp->msg_enable = pcnet32_debug;/// if else 都干了同一件事情 就是 lp->options = PCNET32_PORT_ASEL; (值为0x04)//lp->options用于决定网卡和网线的接口是哪种(AUI,10BAST-T,MII等)///如果已经发现了至少 MAX_UNITS张卡,或者该卡/////////?????????if ((cards_found >= MAX_UNITS)|| (options[cards_found] > sizeof(options_mapping)))lp->options = PCNET32_PORT_ASEL;elselp->options = options_mapping[options[cards_found]];lp->mii_if.dev = dev;/// 由于lp->mii ==0, 所以下面的两个函数没有任何实质性的操作lp->mii_if.mdio_read = mdio_read;lp->mii_if.mdio_write = mdio_write;/* napi.weight is used in both the napi and non-napi cases *///下面这局多余,往下 7行有解释///权重越大,一次中断中能够完成的工作就越多,权重指的是该设备所要准备接收的包数量lp->napi.weight = lp->rx_ring_size / 2;#ifdef CONFIG_PCNET32_NAPI//// pcnet32_poll is a function defined ahead//// 注册poll函数 pcnet32_poll//将(lp->napi)->poll赋为pcnet32_poll。并把第四个参数赋给napi->weightnetif_napi_add(dev, &lp->napi, pcnet32_poll, lp->rx_ring_size / 2);
#endif/// 下面这个if语句块也是多余的,因为 !(lp->options & PCNET32_PORT_ASEL) 为假,原因往前看20行if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&((cards_found >= MAX_UNITS) || full_duplex[cards_found]))lp->options |= PCNET32_PORT_FD;/// 出错处理,如果前面a赋值没有出现问题,不会执行这段if (!a) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_ERR PFX "No access methods\n");ret = -ENODEV;goto err_free_consistent;}lp->a = *a;/* prior to register_netdev, dev->name is not yet correct */if (pcnet32_alloc_ring(dev, pci_name(lp->pci_dev))) {//这实际上使分配发送和接收ring,其值供设置init_block中的rx_ring和tx_ring使用)ret = -ENOMEM;goto err_free_ring;}/* detect special T1/E1 WAN card by checking for MAC address *////推测认为下面的语句也是没有用的/// 这里感觉是Vmware模拟出错,实际测试 mac前24位是00:0c:29 但是amd实际上是00:00:1A//// 但是不管是哪种值,下面的if语句都是不成立的,所以下面的语句是多余的/////??????不知道if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0&& dev->dev_addr[2] == 0x75)lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;/*填写initialization block开始*////对应MODE 0x0003 设置了 两位 1. loopback enable 2.Disable Transmitlp->init_block->mode = cpu_to_le16(0x0003); /* Disable Rx and Tx. Mode初始化的是CSR15*////对应TLEN 和 RLENlp->init_block->tlen_rlen =cpu_to_le16(lp->tx_len_bits | lp->rx_len_bits);///对应PADR 48bitfor (i = 0; i < 6; i++)lp->init_block->phys_addr[i] = dev->dev_addr[i];///对应LADRFlp->init_block->filter[0] = 0x00000000;lp->init_block->filter[1] = 0x00000000;///对应 RDRA和 TDRA, RDRA 是由传输任务描述符组成的数组(tx_ring)的首地址lp->init_block->rx_ring = cpu_to_le32(lp->rx_ring_dma_addr); //// rx_ring_dma_addr的值在pcnet32_alloc_ring中被赋值lp->init_block->tx_ring = cpu_to_le32(lp->tx_ring_dma_addr);/*填写initialization block结束*//* switch pcnet32 to 32bit mode *////下面的设置同时会导致 SSIZE32 (BCR20, bit 8) 为1,最终导致 csr 2 的低16bit组成(存放)IADR的高16位a->write_bcr(ioaddr, 20, 2);//BCR20控制从16位到32位的切换,切换的是software stylea->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));//CSR1和CSR2中存放intial_block的起始地址。a->write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));/// 获如果pci设备没有提供IRQ,调用 proble_irq_on 获取一个位掩码/// 关于 probe_irq_on 驱动程序必须保存返回的为掩码,并且将它传递给后面的probe_irq_off函数,///调用该函数之后(调用probe_irq_on之前),驱动程序要安排设备至少产生一次中断。////如果总线总线系统是vlbus,则pdev为0if (pdev) { /* use the IRQ provided by PCI *////\ 在 Linux 启动时, 计算机的固件已经分配一个唯一的中断号给设备, 并且驱动只需要使用它,就是pdev->irqdev->irq = pdev->irq;if (pcnet32_debug & NETIF_MSG_PROBE)printk(" assigned IRQ %d.\n", dev->irq);} else/////{ ////不知道这里的else有什么意义,如果pdev为假的话,前面获取ioaddr也是错误的,所有操作都没有意义了////下面这段话是为了靠自己获得一个网卡中断号unsigned long irq_mask = probe_irq_on();/** To auto-IRQ we enable the initialization-done and DMA error* interrupts. For ISA boards we get a DMA error, but VLB and PCI* boards will work.*//* Trigger an initialization just for the interrupt. *//// previously defined CSR0 = 0/// #define CSR0_INIT 0x1/// #define CSR0_INTEN 0x40/* INIT assertion enables PCnet-PCI controller to begin the initialization* procedure which reads in the initialization block from memory.INTEN Interrupt Enable allows INTA to be active if the Interrupt Flag isset*//// CSR0_INIT enable 从内存中读取initialblock,根据手册中其他地方的意思,就是开始读取/// CSR0_INTEN 对应IENA位 Interrupt Enable allows INTA to be active if the Interrupt Flag is set./// init block 传输完成后,PCnet-PCI controller 会把 CSR0中的 IDON 置为1,这个动作会产生一个中断a->write_csr(ioaddr, CSR0, CSR0_INTEN | CSR0_INIT);mdelay(1); ///// 延时一秒(忙等待函数),推断是为了给传输init blcok从而产生中断提供时间dev->irq = probe_irq_off(irq_mask); /// 该函数返回前面发生的中断的中断号if (!dev->irq) {if (pcnet32_debug & NETIF_MSG_PROBE)printk(", failed to detect IRQ line.\n");ret = -ENODEV;goto err_free_ring;}if (pcnet32_debug & NETIF_MSG_PROBE)printk(", probed IRQ %d.\n", dev->irq);}/* Set the mii phy_id so that we can query the link state *//// for 79c70A mii =0 ,所以下面的代码忽略///??????if (lp->mii) {/* lp->phycount and lp->phymask are set to 0 by memset above */lp->mii_if.phy_id = ((lp->a.read_bcr(ioaddr, 33)) >> 5) & 0x1f;//BCR33放的是MDIO的起始地址/* scan for PHYs */for (i = 0; i < PCNET32_MAX_PHYS; i++) {/*扫描PHYS*/unsigned short id1, id2;id1 = mdio_read(dev, i, MII_PHYSID1);if (id1 == 0xffff)continue;id2 = mdio_read(dev, i, MII_PHYSID2);if (id2 == 0xffff)continue;if (i == 31 && ((chip_version + 1) & 0xfffe) == 0x2624)continue; /* 79C971 & 79C972 have phantom phy at id 31 */lp->phycount++;lp->phymask |= (1 << i);lp->mii_if.phy_id = i;if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_INFO PFX"Found PHY %04x:%04x at address %d.\n",id1, id2, i);}lp->a.write_bcr(ioaddr, 33, (lp->mii_if.phy_id) << 5);if (lp->phycount > 1) {lp->options |= PCNET32_PORT_MII;}}/** 初始化lp的timer的数据结构。该timer作用是时不时的检查硬件链路状态,若硬件链路断掉,* 如拔掉网线,将通知上层。该通知机制是通过workqueue实现的。*/init_timer(&lp->watchdog_timer);lp->watchdog_timer.data = (unsigned long)dev;lp->watchdog_timer.function = (void *)&pcnet32_watchdog;/* The PCNET32-specific entries in the device structure. *//** int (*open)(struct net_device *dev); * 开启接口** int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev); * 要求硬件开始送出一个封包** int (*stop)(struct net_device *dev); * 停用接口* struct net_device_stats *(*get_stats)(struct net_device *dev); * 每当应用程序需接口统计信息时,就会触动此作业方法.* void (*set_multicast_list)(struct net_device *dev);* 每当装置的群播名单或flags位掩码有任何变动时,就会触发此作业方法.* int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); * 执行特殊的ioctl命令.* void (*tx_timeout)(struct net_device *dev); * 若无法在一段合理时间内完成封包传输,则会呼叫此作业方法.* int watchdog_timeo; * 网络子系统断定传输逾期的最短时间间隔.*/dev->open = &pcnet32_open; ////开启接口dev->hard_start_xmit = &pcnet32_start_xmit; ///要求硬件开始送出一个封包dev->stop = &pcnet32_close; /// 关闭接口dev->get_stats = &pcnet32_get_stats;dev->set_multicast_list = &pcnet32_set_multicast_list;dev->do_ioctl = &pcnet32_ioctl; ////给用户程序提供的控制设备的接口dev->ethtool_ops = &pcnet32_ethtool_ops; ///给ethtool提供的支持dev->tx_timeout = pcnet32_tx_timeout; ////打印错误信息,重启controllerdev->watchdog_timeo = (5 * HZ); ////传输超时的时间值,推测判断超时是上层的任务。#ifdef CONFIG_NET_POLL_CONTROLLER////???? poll_controller作用,由谁来调用,何时会被调用dev->poll_controller = pcnet32_poll_controller;
#endif/* Fill in the generic fields of the device structure. *//// 注册网络设备,可以推测注册成功是返回0的if (register_netdev(dev))goto err_free_ring;///把网络设备指针地址放入PCI设备中的设备指针中///实际上干了 pdev->dev->driver_data = dev;if (pdev) {pci_set_drvdata(pdev, dev);} else {/// defined before: struct net_device *pcnet32_dev//// 不知道这个是什么样的错误处理机制,lp->next = pcnet32_dev;pcnet32_dev = dev;}if (pcnet32_debug & NETIF_MSG_PROBE)printk(KERN_INFO "%s: registered as %s\n", dev->name, lp->name);cards_found++;/* enable LED writes *//// 下面这句推测是错的,因为 他是要往 BCR2 bit 12 写的,但是bit12 was reserved/// 实测写入是不会改变bcr2存储的值的,删掉这句话ping还是可以ping通a->write_bcr(ioaddr, 2, a->read_bcr(ioaddr, 2) | 0x1000);return 0;err_free_ring:pcnet32_free_ring(dev);err_free_consistent:pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block),lp->init_block, lp->init_dma_addr);err_free_netdev:free_netdev(dev);err_release_region:release_region(ioaddr, PCNET32_TOTAL_SIZE);return ret;
}
////pcnet32_alloc_ring() 主要做了3件事,下面详细说明都以tx缓冲为例,rx类似
////1. 给 lp中的tx_ring 和 rx_ring 分配DMA一致映射缓冲区
////2. 分配一个dma_addr_t类型的数组,首地址分别放在 tx_dma_addr中,rx同
////3. 分配一个struc sk_buff 指针的数组,首地址tx_skbuff中,数组大小为 tx_ring_size,rx同,数组元素值均为0
/* if any allocation fails, caller must also call pcnet32_free_ring */
static int pcnet32_alloc_ring(struct net_device *dev, char *name)
{struct pcnet32_private *lp = netdev_priv(dev);/// 分配一致映射DMA缓冲区,缓冲区的大小和ring的大小(即 tx_ring_size 个 tx_head是相同的)lp->tx_ring = pci_alloc_consistent(lp->pci_dev,sizeof(struct pcnet32_tx_head) *lp->tx_ring_size,&lp->tx_ring_dma_addr);if (lp->tx_ring == NULL) {/**#define netif_msg_drv(p) *((p)->msg_enable & NETIF_MSG_DRV) ,就是debug level*/if (netif_msg_drv(lp)) printk("\n" KERN_ERR PFX"%s: Consistent memory allocation failed.\n",name);return -ENOMEM;}lp->rx_ring = pci_alloc_consistent(lp->pci_dev,sizeof(struct pcnet32_rx_head) *lp->rx_ring_size,&lp->rx_ring_dma_addr);if (lp->rx_ring == NULL) {if (netif_msg_drv(lp))printk("\n" KERN_ERR PFX"%s: Consistent memory allocation failed.\n",name);return -ENOMEM;}//// 分配一个数组,param0是数组大小,param1是元素大小,param2是标志位/// GFP_ATOMIC means both !wait (__GFP_WAIT not set) and use emergency pool///\kcalloc — allocate memory for an array. The memory is set to zero.lp->tx_dma_addr = kcalloc(lp->tx_ring_size, sizeof(dma_addr_t),GFP_ATOMIC);if (!lp->tx_dma_addr) {if (netif_msg_drv(lp))printk("\n" KERN_ERR PFX"%s: Memory allocation failed.\n", name);return -ENOMEM;}lp->rx_dma_addr = kcalloc(lp->rx_ring_size, sizeof(dma_addr_t),GFP_ATOMIC);if (!lp->rx_dma_addr) {if (netif_msg_drv(lp))printk("\n" KERN_ERR PFX"%s: Memory allocation failed.\n", name);return -ENOMEM;}/// tx_skbuff 的意思是套接字缓冲区,lp->tx_skbuff是 struct sk_buff** 类型的数据lp->tx_skbuff = kcalloc(lp->tx_ring_size, sizeof(struct sk_buff *),GFP_ATOMIC);if (!lp->tx_skbuff) {if (netif_msg_drv(lp))printk("\n" KERN_ERR PFX"%s: Memory allocation failed.\n", name);return -ENOMEM;}lp->rx_skbuff = kcalloc(lp->rx_ring_size, sizeof(struct sk_buff *),GFP_ATOMIC);if (!lp->rx_skbuff) {if (netif_msg_drv(lp))printk("\n" KERN_ERR PFX"%s: Memory allocation failed.\n", name);return -ENOMEM;}return 0;
}/// 释放pcnte32_alloc_ring()中分配的内存空间,并且将相关的指针置为NULL
static void pcnet32_free_ring(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);kfree(lp->tx_skbuff);lp->tx_skbuff = NULL;kfree(lp->rx_skbuff);lp->rx_skbuff = NULL;kfree(lp->tx_dma_addr);lp->tx_dma_addr = NULL;kfree(lp->rx_dma_addr);lp->rx_dma_addr = NULL;if (lp->tx_ring) {pci_free_consistent(lp->pci_dev,sizeof(struct pcnet32_tx_head) *lp->tx_ring_size, lp->tx_ring,lp->tx_ring_dma_addr);lp->tx_ring = NULL;}if (lp->rx_ring) {pci_free_consistent(lp->pci_dev,sizeof(struct pcnet32_rx_head) *lp->rx_ring_size, lp->rx_ring,lp->rx_ring_dma_addr);lp->rx_ring = NULL;}
}/*****总结:注册中断程序,初始化网卡,分配相关缓冲区,设置运行参数* 1.注册设备的中断服务程序* 2.reset the pcnet32 controller and switch pcnet32 to 32bit mode* 3.reset the auto-selecte bit(int BCR2),并且单网卡的情况下,最后置的* 还是auto-selected mod即有controller自动选择下层 接口* 4.handle full duplex setting,单网卡下,设置 half duplex operation,且不使用AUI port* 5.设置GPSI,单个网卡下,GPSI为假* 6.phy相关设置,但是由于程序bug,没有执行有意义的操作* 7.设置lp->init_block->mode,单网卡情况下该值为0,即不改变网卡操作模式* 8.载入多播表(这个目前还不是很清楚)* 9.调用pcnet32_init_ring初始化 lp->rx_skbuff数组,并为每个sk_buff分配缓冲区,建立DMA映射* 10.enable napi* 11.Re-initialize the PCNET32, and start it when done* 12.Print the link status /这个函数没有细看了,因为如果* 13.start the watchdog* 14.出错处理*/
static int pcnet32_open(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long ioaddr = dev->base_addr;u16 val;int i;int rc;unsigned long flags;///irq启动时BIOS已经完成分配,这里是注册这个irq的中断服务程序,if (request_irq(dev->irq, &pcnet32_interrupt,lp->shared_irq ? IRQF_SHARED : 0, dev->name,(void *)dev)) {return -EAGAIN;}spin_lock_irqsave(&lp->lock, flags);/* Check for a valid station address */if (!is_valid_ether_addr(dev->dev_addr)) {rc = -EINVAL;goto err_free_irq;}/* Reset the PCNET32 */lp->a.reset(ioaddr); ///\Reset causes the device to cease operation and clear its internal logic./* switch pcnet32 to 32bit mode */////这里是指切换到32bit的software stylelp->a.write_bcr(ioaddr, 20, 2);///\ 判断 lp->meg_enableif (netif_msg_ifup(lp))printk(KERN_DEBUG"%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",dev->name, dev->irq, (u32) (lp->tx_ring_dma_addr),(u32) (lp->rx_ring_dma_addr),(u32) (lp->init_dma_addr));/**** While in auto-selection mode, the interface in use is determined by an auto-sensing mechanism which checks the* link status on the 10BASE-T port. If there is no active link status, then the device assumes an AUI connection*bcr2 bit1置0,*//* set/reset autoselect bit */val = lp->a.read_bcr(ioaddr, 2) & ~2;///\如果lp->options是 PCNET32_PORT_ASEL,则BCR2,bit1写入1,设置auto select,在只有一个芯片的情况下,是auto-selecteif (lp->options & PCNET32_PORT_ASEL)val |= 2;lp->a.write_bcr(ioaddr, 2, val);/* handle full duplex setting */if (lp->mii_if.full_duplex) {val = lp->a.read_bcr(ioaddr, 9) & ~3; //val的bit0 和bit1 置0///\因为只有一个网卡时options == PCNET32_PROT_ASEL所以下面的语句都不会执行if (lp->options & PCNET32_PORT_FD) {val |= 1;if (lp->options == (PCNET32_PORT_FD | PCNET32_PORT_AUI))val |= 2;} else if (lp->options & PCNET32_PORT_ASEL) {/* workaround of xSeries250, turn on for 79C975 only */if (lp->chip_version == 0x2627)val |= 3;}///\ BCR9: Full-Duplex Control,bit0,bit1 置0,设置 half duplex operation,且不使用AUI portlp->a.write_bcr(ioaddr, 9, val);}/* set/reset GPSI bit in test register *////\ bit4 GPSIEN:General Purpose Serial Interface Enable.,GPSI is used as an interface between Ethernet MAC and PHY blocks.val = lp->a.read_csr(ioaddr, 124) & ~0x10;///\ 单个网卡芯片,条件为否,所以GPSI为假if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI)val |= 0x10;lp->a.write_csr(ioaddr, 124, val);/* Allied Telesyn AT 2700/2701 FX are 100Mbit only and do not negotiate *//** 这个不是编写AMD的网卡么,AT怎么乱入了。。。。推测可能他们两有一腿,不过由于79c970A* * vendor_id(即subsystem_vendor)为1022h,The PCnet-PCI II controller DeviceID * is 2000h,插define可知,和下面不一样,所以判断语句为假*/if (lp->pci_dev->subsystem_vendor == PCI_VENDOR_ID_AT &&(lp->pci_dev->subsystem_device == PCI_SUBDEVICE_ID_AT_2700FX ||lp->pci_dev->subsystem_device == PCI_SUBDEVICE_ID_AT_2701FX)) {if (lp->options & PCNET32_PORT_ASEL) {lp->options = PCNET32_PORT_FD | PCNET32_PORT_100;if (netif_msg_link(lp))printk(KERN_DEBUG"%s: Setting 100Mb-Full Duplex.\n",dev->name);}}//// lp->phycount表示phy的数量/** 由于mii==0,所以在pcnet32_probe1中,这个值就没有改过,推测alloc_etherdev时,*可能自动识别了phy的数量,vmware下lp->phycount==0*////下面的if-else在当前情况(VMWARE)配置下什么都没有干,实际运行了的执行语句,还是错误的,if (lp->phycount < 2) {/** 24 Jun 2004 according AMD, in order to change the PHY,* DANAS (or DISPM for 79C976) must be set; then select the speed,* duplex, and/or enable auto negotiation, and clear DANAS*////\ mii ==0if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {lp->a.write_bcr(ioaddr, 32,lp->a.read_bcr(ioaddr, 32) | 0x0080);/* disable Auto Negotiation, set 10Mpbs, HD */val = lp->a.read_bcr(ioaddr, 32) & ~0xb8;if (lp->options & PCNET32_PORT_FD)val |= 0x10;if (lp->options & PCNET32_PORT_100)val |= 0x08;lp->a.write_bcr(ioaddr, 32, val);} else {/** 额。。。。BCR32是不存在的。。。所以下面的语句没有任何价值,不过考虑到驱动工作正常,* 所以好像不会造成什么disaster。*/if (lp->options & PCNET32_PORT_ASEL) {lp->a.write_bcr(ioaddr, 32,lp->a.read_bcr(ioaddr,32) | 0x0080);/* enable auto negotiate, setup, disable fd */val = lp->a.read_bcr(ioaddr, 32) & ~0x98;val |= 0x20;lp->a.write_bcr(ioaddr, 32, val);}}///判断句条件为真,所以else不会执行} else {int first_phy = -1;u16 bmcr;u32 bcr9;struct ethtool_cmd ecmd;/** There is really no good other way to handle multiple PHYs* other than turning off all automatics*/val = lp->a.read_bcr(ioaddr, 2);lp->a.write_bcr(ioaddr, 2, val & ~2);val = lp->a.read_bcr(ioaddr, 32);lp->a.write_bcr(ioaddr, 32, val & ~(1 << 7)); /* stop MII manager */if (!(lp->options & PCNET32_PORT_ASEL)) {/* setup ecmd */ecmd.port = PORT_MII;ecmd.transceiver = XCVR_INTERNAL;ecmd.autoneg = AUTONEG_DISABLE;ecmd.speed =lp->options & PCNET32_PORT_100 ? SPEED_100 : SPEED_10;bcr9 = lp->a.read_bcr(ioaddr, 9);if (lp->options & PCNET32_PORT_FD) {ecmd.duplex = DUPLEX_FULL;bcr9 |= (1 << 0);} else {ecmd.duplex = DUPLEX_HALF;bcr9 |= ~(1 << 0);}lp->a.write_bcr(ioaddr, 9, bcr9);}for (i = 0; i < PCNET32_MAX_PHYS; i++) {if (lp->phymask & (1 << i)) {/* isolate all but the first PHY */bmcr = mdio_read(dev, i, MII_BMCR);if (first_phy == -1) {first_phy = i;mdio_write(dev, i, MII_BMCR,bmcr & ~BMCR_ISOLATE);} else {mdio_write(dev, i, MII_BMCR,bmcr | BMCR_ISOLATE);}/* use mii_ethtool_sset to setup PHY */lp->mii_if.phy_id = i;ecmd.phy_address = i;if (lp->options & PCNET32_PORT_ASEL) {mii_ethtool_gset(&lp->mii_if, &ecmd);ecmd.autoneg = AUTONEG_ENABLE;}mii_ethtool_sset(&lp->mii_if, &ecmd);}}lp->mii_if.phy_id = first_phy;if (netif_msg_link(lp))printk(KERN_INFO "%s: Using PHY number %d.\n",dev->name, first_phy);}#ifdef DO_DXSUFLO///for 79C970A lp->dxsuflo==0,所以条件为假if (lp->dxsuflo) { /* Disable transmit stop on underflow */val = lp->a.read_csr(ioaddr, CSR3);val |= 0x40;lp->a.write_csr(ioaddr, CSR3, val);}
#endif///\PCNET32_PORT_PORTSEL = 0x03, options=0x04,so mode = 0;lp->init_block->mode =cpu_to_le16((lp->options & PCNET32_PORT_PORTSEL) << 7);///TODO\功能推测就是产生filter的值,然后填入对应的CSR中,pcnet32_load_multicast(dev);///init_ring初始化 lp->rx_skbuff数组,并为每个sk_buff分配缓冲区,建立DMA映射if (pcnet32_init_ring(dev)) {rc = -ENOMEM;goto err_free_ring;}#ifdef CONFIG_PCNET32_NAPI/// enable napinapi_enable(&lp->napi);
#endif/* Re-initialize the PCNET32, and start it when done. */lp->a.write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));lp->a.write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));/**DM* 下面不应该有09* CSR4: Test and Features Control,屏蔽Jabber Error,Transmit Start,Receive Collision Counter Overflow产生的中断*/lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad *////INIT 启动init block从内存写入网卡lp->a.write_csr(ioaddr, CSR0, CSR0_INIT);////start the interface's transmit queue,驱动程序调用这个函数来告诉内核网络子系统,现在可以开始数据包的发送。netif_start_queue(dev);////vmware配备的网卡是70c970Aif (lp->chip_version >= PCNET32_79C970A) {/* Print the link status and start the watchdog */pcnet32_check_media(dev, 1);/* mod_timer - modify a timer's timeout* @timer: the timer to be modified* @expires: new timeout in jiffies** mod_timer() is a more efficient way to update the expire field of an* active timer (if the timer is inactive it will be activated)*////#define PCNET32_WATCHDOG_TIMEOUT (jiffies + (2 * HZ)) 不明白为什么要用这个作为expire值,/// timeout是一个非常大的数,所以推测作者只是想用一下mod_timer的active the timer的功能mod_timer(&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);}i = 0;////循环等待,当init block写入网卡寄存器完成,contorller会自动将CSR0_IDON位置1,该位由0变为1时就说明init block传输完成了while (i++ < 100)if (lp->a.read_csr(ioaddr, CSR0) & CSR0_IDON)break;/** We used to clear the InitDone bit, 0x0100, here but Mark Stockton* reports that doing so triggers a bug in the '974.*//// CSR0_NORMAL = (CSR0_START | CSR0_INTEN), 下面讲 CSR0中的STRT位和EINA位置1////STRT位,该位置1表示启动网卡控制器发送和接受帧,并开始执行缓冲区管理操作,为1表示不能接受发送数据////IENA位,该位为1表示允许中断,该位为0表示关中断,IENA置1时还会将CSR0的STOP位清0lp->a.write_csr(ioaddr, CSR0, CSR0_NORMAL);if (netif_msg_ifup(lp))printk(KERN_DEBUG"%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n",dev->name, i,(u32) (lp->init_dma_addr),lp->a.read_csr(ioaddr, CSR0));spin_unlock_irqrestore(&lp->lock, flags);return 0; /* Always succeed */err_free_ring:/* free any allocated skbuffs */pcnet32_purge_rx_ring(dev);/** Switch back to 16bit mode to avoid problems with dumb* DOS packet driver after a warm reboot*/lp->a.write_bcr(ioaddr, 20, 4);err_free_irq:spin_unlock_irqrestore(&lp->lock, flags);free_irq(dev->irq, dev);return rc;
}/** The LANCE has been halted for one reason or another (busmaster memory* arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,* etc.). Modern LANCE variants always reload their ring-buffer* configuration when restarted, so we must reinitialize our ring* context before restarting. As part of this reinitialization,* find all packets still on the Tx ring and pretend that they had been* sent (in effect, drop the packets on the floor) - the higher-level* protocols will time out and retransmit. It'd be better to shuffle* these skbs to a temp list and then actually re-Tx them after* restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com*////取消tx_ring每个descriptor的数据缓冲区的DMA映射(如果有缓冲区的话),并释放缓冲区
static void pcnet32_purge_tx_ring(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);int i;for (i = 0; i < lp->tx_ring_size; i++) {lp->tx_ring[i].status = 0; /* CPU owns buffer */wmb(); /* Make sure adapter sees owner change */if (lp->tx_skbuff[i]) {pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],lp->tx_skbuff[i]->len,PCI_DMA_TODEVICE);dev_kfree_skb_any(lp->tx_skbuff[i]);}lp->tx_skbuff[i] = NULL;lp->tx_dma_addr[i] = 0;}
}/* Initialize the PCNET32 Rx and Tx rings. */
/**** 1.为rx_skbuff数组每个元素分配缓冲区,并建立DMA映射FROM_DEVICE所以该缓冲区仅仅,注意* rx_skbuff的元素数量是和rx_ring_size相同的,所以推测他们之间应该是一一对应的关系,* 2.tx_skbuff需要使用时才分配缓冲区* 3.再次填充lp 中的init block,原因不明,而且填充的值都没有变过,所以感觉这里有点多余*/
static int pcnet32_init_ring(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);int i;lp->tx_full = 0;lp->cur_rx = lp->cur_tx = 0;lp->dirty_rx = lp->dirty_tx = 0;///\ rx_ring_size是在probe时指定的,并写入init block了for (i = 0; i < lp->rx_ring_size; i++) {struct sk_buff *rx_skbuff = lp->rx_skbuff[i];/** lp->rx_skbuff初始值均为0,下面是为lp->rx_skbuff初始化,* 为每个数据元素分配缓冲区,并是以太网首部对其*/if (rx_skbuff == NULL) {if (!(rx_skbuff = lp->rx_skbuff[i] =dev_alloc_skb(PKT_BUF_SKB))) {/* there is not much, we can do at this point */if (netif_msg_drv(lp))printk(KERN_ERR"%s: pcnet32_init_ring dev_alloc_skb failed.\n",dev->name);return -1;}/// 预留出NET_IP_ALIGN长度的首部,因为以太网头部14bity,这里的宏为2,正好16字节,边界对其(4字节)skb_reserve(rx_skbuff, NET_IP_ALIGN);}rmb();/** 为每一个时sk_buff的packet数据区(sk_buff中的而一个用来存放packet的区)建立DMA映射,* 并将DMA映射地址放在dma_addr数组中,PCI_DAM_FROMDEVICE表明该缓冲区只是 * 用来给device写入的,umap之后驱动才能读取里面的数据*/if (lp->rx_dma_addr[i] == 0)lp->rx_dma_addr[i] =pci_map_single(lp->pci_dev, rx_skbuff->data,PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);lp->rx_ring[i].base = cpu_to_le32(lp->rx_dma_addr[i]);lp->rx_ring[i].buf_length = cpu_to_le16(NEG_BUF_SIZE);wmb(); /* Make sure owner changes after all others are visible */lp->rx_ring[i].status = cpu_to_le16(0x8000); //将own位置1,contorller为owner}/* The Tx buffer address is filled in as needed, but we do need to clear* the upper ownership bit. */for (i = 0; i < lp->tx_ring_size; i++) {lp->tx_ring[i].status = 0; /* CPU owns buffer */wmb(); /* Make sure adapter sees owner change */lp->tx_ring[i].base = 0;lp->tx_dma_addr[i] = 0;}///prob\还不清楚为什么要在这里再次填充init blocklp->init_block->tlen_rlen =cpu_to_le16(lp->tx_len_bits | lp->rx_len_bits);for (i = 0; i < 6; i++)lp->init_block->phys_addr[i] = dev->dev_addr[i];lp->init_block->rx_ring = cpu_to_le32(lp->rx_ring_dma_addr);lp->init_block->tx_ring = cpu_to_le32(lp->tx_ring_dma_addr);wmb(); /* Make sure all changes are visible */return 0;
}////restart
/* the pcnet32 has been issued a stop or reset. Wait for the stop bit* then flush the pending transmit operations, re-initialize the ring,* and tell the chip to initialize.*/
static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long ioaddr = dev->base_addr;int i;/* wait for stop */for (i = 0; i < 100; i++)if (lp->a.read_csr(ioaddr, CSR0) & CSR0_STOP)break;///等待超时,打印错误信息if (i >= 100 && netif_msg_drv(lp))printk(KERN_ERR"%s: pcnet32_restart timed out waiting for stop.\n",dev->name);////释放tx_ring持有的资源,缓冲区和DMA映射pcnet32_purge_tx_ring(dev);////(重新)初始化tx 和 rx ringif (pcnet32_init_ring(dev))return;/* * 如果上面的初始化失败,则 prob/// 不清楚为什么下面的能reinit,* 感觉只是重新了initblock,然后设置一下网卡,tx_ring rx_ring 并没有回来啊*//* ReInit Ring */ /* INIT assertion enables the PCnet-PCI II controller to begin the initialization * procedure which reads the initialization block from memory.* lp->a.write_csr(ioaddr, CSR0, CSR0_INIT);*/i = 0;////等待init写入网卡完成while (i++ < 1000)if (lp->a.read_csr(ioaddr, CSR0) & CSR0_IDON)break;///把参数写入寄存器lp->a.write_csr(ioaddr, CSR0, csr0_bits);
}
///主要用于内核调用它解决发送超时的问题,超时可能的原因是设备故障或者传输中线路中断等,解决的方法大多数时候是把硬件设备复位。
///下面的网卡做的事情就是发送超时时,reset contorller。并打印相关错误信息
static void pcnet32_tx_timeout(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long ioaddr = dev->base_addr, flags;spin_lock_irqsave(&lp->lock, flags);/* Transmitter timeout, serious problems. */if (pcnet32_debug & NETIF_MSG_DRV)printk(KERN_ERR"%s: transmit timed out, status %4.4x, resetting.\n",dev->name, lp->a.read_csr(ioaddr, CSR0));lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);dev->stats.tx_errors++;if (netif_msg_tx_err(lp)) {int i;printk(KERN_DEBUG" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",lp->cur_rx);for (i = 0; i < lp->rx_ring_size; i++)printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",le32_to_cpu(lp->rx_ring[i].base),(-le16_to_cpu(lp->rx_ring[i].buf_length)) &0xffff, le32_to_cpu(lp->rx_ring[i].msg_length),le16_to_cpu(lp->rx_ring[i].status));for (i = 0; i < lp->tx_ring_size; i++)printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",le32_to_cpu(lp->tx_ring[i].base),(-le16_to_cpu(lp->tx_ring[i].length)) & 0xffff,le32_to_cpu(lp->tx_ring[i].misc),le16_to_cpu(lp->tx_ring[i].status));printk("\n");}pcnet32_restart(dev, CSR0_NORMAL);dev->trans_start = jiffies;netif_wake_queue(dev);spin_unlock_irqrestore(&lp->lock, flags);
}/** 函数功能:* 1.把sk_buff的长度、基地址、数据缓冲区地址等放入tx_ring,tx_skbuff等领中,* 2.设置 descriptor的status 、misc值,目前具体各位干嘛的还没细看,但是代码填的值肯定是合适的* 3.写CSR0,设置允许中断,并启动数据包发送* 4.判断发送队列是否已经满了,满了就进行流量控制.(即通知网络子系统暂停数据包传输)* 5. 没有发现字节不够时,填充数据的行为,这个是网卡硬件自动完成的??///prob*/
static int pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev) //发包时会调用该函数
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long ioaddr = dev->base_addr;u16 status;int entry;unsigned long flags;/** 两个功能,* 1,自旋锁的功能,* 2.关中断 * flag用于记录当前中断的状态,用于unlock时恢复之前的CPU中断状态(原来* 如果是关的就还是关的)spin_lock_irqsave(&lp->lock, flags);*//// 下面的函数是个宏 等于((lp)->msg_enable & NETIF_MSG_TX_QUEUED),debug level的事情if (netif_msg_tx_queued(lp)) {printk(KERN_DEBUG"%s: pcnet32_start_xmit() called, csr0 %4.4x.\n",dev->name, lp->a.read_csr(ioaddr, CSR0));}/* Default status -- will not enable Successful-TxDone* interrupt when that option is available to us.*/status = 0x8300;/* Fill in a Tx ring entry *//* Mask to ring buffer boundary. */entry = lp->cur_tx & lp->tx_mod_mask;/* Caution: the write order is important here, set the status* with the "ownership" bits last. */lp->tx_ring[entry].length = cpu_to_le16(-skb->len);//填写length的补码 额。。这里有点奇怪啊。。lp->tx_ring[entry].misc = 0x00000000; ////对应TMD2,具体含义暂时不看lp->tx_skbuff[entry] = skb;lp->tx_dma_addr[entry] =pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE);//将地址翻译成物理地址lp->tx_ring[entry].base = cpu_to_le32(lp->tx_dma_addr[entry]);wmb(); /* Make sure owner changes after all others are visible */ //memory barrier///其他位暂时不管,反正这么填就对了lp->tx_ring[entry].status = cpu_to_le16(status);//将own位置1,即将ring buffer控制权交给网卡。lp->cur_tx++;dev->stats.tx_bytes += skb->len;/* Trigger an immediate send poll. *///CSR0_TXPOLL=0x8,CSR0_INTEN=0x40 ,即将第3位和第6位置1。即触发传输包,并使网卡中断生效lp->a.write_csr(ioaddr, CSR0, CSR0_INTEN | CSR0_TXPOLL);dev->trans_start = jiffies; //DM/ jiffies是内核全局变量,表示系统启动以来是clock tick次数///netif_stop_queue一般用于驱动程序通知网络子系统暂停数据包传输,从来进行实现流量控制,判断的逻辑还是不太明白///是个环,检测到下一个不为空,说明已经满了if (lp->tx_ring[(entry + 1) & lp->tx_mod_mask].base != 0) {lp->tx_full = 1;netif_stop_queue(dev);}spin_unlock_irqrestore(&lp->lock, flags);return 0;
}/**DM* 这是中断服务程序* 1.读取csr0,判断有没有错误发生,有的话进行错误处理* 2.写CSR3,屏蔽中断,如果NAPI没有被禁止,且不存在已被调度的NAPI,则调度NAPI(* napi_struct结构挂入全局的poll_list中。并生成一个软中断。)* Qes: 为什么这里使用spin_lock而不用使用 spin_lock_irqsave()机制*/
/* The PCNET32 interrupt handler. */
static irqreturn_t
pcnet32_interrupt(int irq, void *dev_id)
{struct net_device *dev = dev_id;struct pcnet32_private *lp;unsigned long ioaddr;u16 csr0;/// max_interrupt_work 默认值为2,insmods时可以传参数改变,推测是NAPI用来作为切换中断模式和轮询模式的接线/// boguscnt是用来计数的int boguscnt = max_interrupt_work;ioaddr = dev->base_addr;lp = netdev_priv(dev);spin_lock(&lp->lock);csr0 = lp->a.read_csr(ioaddr, CSR0);///CSR0第15位置1表示网卡出错了。第11位置1表示Memory Error,第10位置1表示收到收包中断。第9位置1表示收到controller的发包中断,///第8位是IDON位,init block传输完成之后这一位就一直是1了) 所以这段代码用来处理中断和错误while ((csr0 & 0x8f00) && --boguscnt >= 0) { /// 网卡没有错误,并且还有任务处理配额/////?????含义if (csr0 == 0xffff) {break; /* PCMCIA remove happened */ ///不清楚PCMCIA具体指什么,可能是指网卡被拔了}/* Acknowledge all of the current interrupt sources ASAP. */ //ASAP = as soon as possible/**CSR0的 bit0~bit5 * 写0是无法写入的,也不会产生影响,所以下面的代码只会置INEA(interrupt enable)为0,即禁止中断防止网卡上的中断重入* lp->a.write_csr(ioaddr, CSR0, csr0 & ~0x004f);*////出错处理开始(出错处理就是打印一些信息,或者对错误计数器进行累加)//#define netif_msg_intr(p) ((p)->msg_enable & NETIF_MSG_INTR),就是进行debug devel的判断if (netif_msg_intr(lp))printk(KERN_DEBUG"%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",dev->name, csr0, lp->a.read_csr(ioaddr, CSR0));/* Log misc errors. *///CSR0第14位是reserve位。搞不懂,这个地方是一个bug,应该是0x8000才对。因为手册上15位才是ERR位。if (csr0 & 0x4000)dev->stats.tx_errors++; /* Tx babble. *////bit12 MISS位 Missed Frame is set by the PCnet-PCI II controller when it looses an incoming receive frame///because a receive descriptorwas not availableif (csr0 & 0x1000) {//CSR0 第12位为1,表示丢失了一个incoming frame/** This happens when our receive ring is full. This* shouldn't be a problem as we will see normal rx* interrupts for the frames in the receive ring. But* there are some PCI chipsets (I can reproduce this* on SP3G with Intel saturn chipset) which have* sometimes problems and will fill up the receive* ring with error descriptors. In this situation we* don't get a rx interrupt, but a missed frame* interrupt sooner or later.*/dev->stats.rx_errors++; /* Missed a Rx frame. */}if (csr0 & 0x0800) {//CSR0第11位置1表示Memory Error。if (netif_msg_drv(lp))//#define netif_msg_drv(p) ((p)->msg_enable & NETIF_MSG_DRV),即判断是否写logprintk(KERN_ERR"%s: Bus master arbitration failure, status %4.4x.\n",dev->name, csr0);/* unlike for the lance, there is no restart needed */}////出错处理结束
#ifdef CONFIG_PCNET32_NAPI/** netif_rx_schedule_prep的实现只有一句:return napi_schedule_prep(napi);* 也就是只有&lp->napi管作用,napi_schedule_prep也只是查看一下napi中的state中的* NAPI_STATE_DISABLE位是否被置。如果没有被置的话,则设置NAPI_STATE_SCHED位。即允许napi。*//** netif_rx_schedule_prep的功能:* Test if NAPI routine is already running, and if not mark it as running.* This is used as a condition variable insure only one NAPI poll instance* runs. We also make sure there is no pending NAPI disable.*/if (netif_rx_schedule_prep(dev, &lp->napi)) {u16 val;/* set interrupt masks */val = lp->a.read_csr(ioaddr, CSR3);//CSR3中存放的是中断屏蔽字以及deferrable control/**DM* 0x5f00设置bit 8到12,和14位,置1* bit14 屏蔽 babble错误 Babble is a transmitter time-out error* bit12 Missed Frame Mask.* bit11 Memory Error Mask* bit10 Receive Interrupt Mask* bit9 Transmit Interrupt Mask* bit8 Initialization Done Mask* CSR3上的屏蔽字就这么几个*/val |= 0x5f00;///屏蔽中断lp->a.write_csr(ioaddr, CSR3, val);mmiowb(); //这是memory_barrier,但x86实现其为空//其实现只有一句话: __napi_schedule(napi);,即将lp中的napi_struct结构挂入全局的poll_list中。并生成一个软中断。///这里到底挂上去有哪些内容不清楚,////??????看看napi 软中断 具体实现机制,然后再注释,把对napi的理解写上去__netif_rx_schedule(dev, &lp->napi);/*void __napi_schedule(struct napi_struct *n){unsigned long flags;local_irq_save(flags);list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);__raise_softirq_irqoff(NET_RX_SOFTIRQ);local_irq_restore(flags);}//注意:这里的off不是关闭,而是offset的意思。#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) */break;}
#else//DM/下面是没有napi时的运行选项pcnet32_rx(dev, lp->napi.weight);if (pcnet32_tx(dev)) {/* reset the chip to clear the error condition, then restart */lp->a.reset(ioaddr);lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */pcnet32_restart(dev, CSR0_START);netif_wake_queue(dev);}
#endifcsr0 = lp->a.read_csr(ioaddr, CSR0);}#ifndef CONFIG_PCNET32_NAPI/* Set interrupt enable. */////CSR0只有 bit6写1,其他位写0都是无效的lp->a.write_csr(ioaddr, CSR0, CSR0_INTEN);//开中断
#endifif (netif_msg_intr(lp))printk(KERN_DEBUG "%s: exiting interrupt, csr0=%#4.4x.\n",dev->name, lp->a.read_csr(ioaddr, CSR0));spin_unlock(&lp->lock);return IRQ_HANDLED;
}/**** close the controller* 关闭timer,disable napi,设置相关寄存器值,释放申请的资源*/
static int pcnet32_close(struct net_device *dev)
{unsigned long ioaddr = dev->base_addr;struct pcnet32_private *lp = netdev_priv(dev);unsigned long flags;///deactivate a timer and wait for the handler to finish.del_timer_sync(&lp->watchdog_timer);netif_stop_queue(dev);
#ifdef CONFIG_PCNET32_NAPInapi_disable(&lp->napi);
#endifspin_lock_irqsave(&lp->lock, flags);dev->stats.rx_missed_errors = lp->a.read_csr(ioaddr, 112);if (netif_msg_ifdown(lp))printk(KERN_DEBUG"%s: Shutting down ethercard, status was %2.2x.\n",dev->name, lp->a.read_csr(ioaddr, CSR0));/* We stop the PCNET32 here -- it occasionally polls memory if we don't. */lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);/** Switch back to 16bit mode to avoid problems with dumb* DOS packet driver after a warm reboot*/lp->a.write_bcr(ioaddr, 20, 4);spin_unlock_irqrestore(&lp->lock, flags);free_irq(dev->irq, dev);spin_lock_irqsave(&lp->lock, flags);pcnet32_purge_rx_ring(dev);pcnet32_purge_tx_ring(dev);spin_unlock_irqrestore(&lp->lock, flags);return 0;
}static struct net_device_stats *pcnet32_get_stats(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long ioaddr = dev->base_addr;unsigned long flags;spin_lock_irqsave(&lp->lock, flags);dev->stats.rx_missed_errors = lp->a.read_csr(ioaddr, 112);spin_unlock_irqrestore(&lp->lock, flags);return &dev->stats;
}///todo\具体实现逻辑还没有理解,不过可以大概看出这个函数的逻辑就是产生新的filter,然后
///将其写入对应的CSR中,具体逻辑还需要继续研究,先放着
/* taken from the sunlance driver, which it took from the depca driver */
static void pcnet32_load_multicast(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);volatile struct pcnet32_init_block *ib = lp->init_block;volatile __le16 *mcast_table = (__le16 *)ib->filter;struct dev_mc_list *dmi = dev->mc_list;unsigned long ioaddr = dev->base_addr;char *addrs;int i;u32 crc;/* set all multicast bits *//**** 驱动中没有看到dev->flags的赋值语句,推测可能是有默认值,实测为0x1002* #define IFF_ALLMULTI 0x200 /// receive all multicast packets* 因为flags的值没有被改过,所以条件为假*/if (dev->flags & IFF_ALLMULTI) {ib->filter[0] = cpu_to_le32(~0U);ib->filter[1] = cpu_to_le32(~0U);lp->a.write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff);lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff);lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff);lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff);return;}/* clear the multicast filter */ib->filter[0] = 0;ib->filter[1] = 0;///todo\下面添加多播地址的逻辑让人有点费解,但是可以确定他的功能就是添加多播地址,并将新的logitc address/////filter写入对应的CSR中/* Add addresses */for (i = 0; i < dev->mc_count; i++) {addrs = dmi->dmi_addr;dmi = dmi->next;/* multicast address? *////\不是则回到循环开始,是就继续往后走,这里认为bit47为1为多播地址(2012年后已经改为bit0为1)///\需要注意的一个问题是 struct dev_mc_list.dmi_addr只有8位,原因不明if (!(*addrs & 1))continue;///\地址如果识别是逻辑地址,则进行CRC计算,产生filter位号(6bits)///,用来指定filter中的位置(64个位置),如果对应位为1,那么接受该地址的包///\参数6表示值使用addrs的低六位crc = ether_crc_le(6, addrs);crc = crc >> 26; ////只需要crc的高六位mcast_table[crc >> 4] |= cpu_to_le16(1 << (crc & 0xf));}for (i = 0; i < 4; i++)lp->a.write_csr(ioaddr, PCNET32_MC_FILTER + i,le16_to_cpu(mcast_table[i]));return;
}/** Set or clear the multicast filter for this adaptor.*/
static void pcnet32_set_multicast_list(struct net_device *dev)
{unsigned long ioaddr = dev->base_addr, flags;struct pcnet32_private *lp = netdev_priv(dev);int csr15, suspended;spin_lock_irqsave(&lp->lock, flags);suspended = pcnet32_suspend(dev, &flags, 0);csr15 = lp->a.read_csr(ioaddr, CSR15);if (dev->flags & IFF_PROMISC) {/* Log any net taps. */if (netif_msg_hw(lp))printk(KERN_INFO "%s: Promiscuous mode enabled.\n",dev->name);lp->init_block->mode =cpu_to_le16(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) <<7);lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000);} else {lp->init_block->mode =cpu_to_le16((lp->options & PCNET32_PORT_PORTSEL) << 7);lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff);pcnet32_load_multicast(dev);}if (suspended) {int csr5;/* clear SUSPEND (SPND) - CSR5 bit 0 */csr5 = lp->a.read_csr(ioaddr, CSR5);lp->a.write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));} else {lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);pcnet32_restart(dev, CSR0_NORMAL);netif_wake_queue(dev);}spin_unlock_irqrestore(&lp->lock, flags);
}/* This routine assumes that the lp->lock is held */
////下面的函数做的是mii读相关的工作,但是因为 lp->mii == 0, 所以什么都不会做
static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long ioaddr = dev->base_addr;u16 val_out;if (!lp->mii)return 0;lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));val_out = lp->a.read_bcr(ioaddr, 34);return val_out;
}/* This routine assumes that the lp->lock is held */
////下面的函数做的是mii写相关的工作,但是因为 lp->mii == 0, 所以什么都不会做
static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long ioaddr = dev->base_addr;if (!lp->mii)return;lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));lp->a.write_bcr(ioaddr, 34, val);
}static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{struct pcnet32_private *lp = netdev_priv(dev);int rc;unsigned long flags;/* SIOC[GS]MIIxxx ioctls */if (lp->mii) {spin_lock_irqsave(&lp->lock, flags);rc = generic_mii_ioctl(&lp->mii_if, if_mii(rq), cmd, NULL);spin_unlock_irqrestore(&lp->lock, flags);} else {rc = -EOPNOTSUPP;}return rc;
}static int pcnet32_check_otherphy(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);struct mii_if_info mii = lp->mii_if;u16 bmcr;int i;for (i = 0; i < PCNET32_MAX_PHYS; i++) {if (i == lp->mii_if.phy_id)continue; /* skip active phy */if (lp->phymask & (1 << i)) {mii.phy_id = i;if (mii_link_ok(&mii)) {/* found PHY with active link */if (netif_msg_link(lp))printk(KERN_INFO"%s: Using PHY number %d.\n",dev->name, i);/* isolate inactive phy */bmcr =mdio_read(dev, lp->mii_if.phy_id, MII_BMCR);mdio_write(dev, lp->mii_if.phy_id, MII_BMCR,bmcr | BMCR_ISOLATE);/* de-isolate new phy */bmcr = mdio_read(dev, i, MII_BMCR);mdio_write(dev, i, MII_BMCR,bmcr & ~BMCR_ISOLATE);/* set new phy address */lp->mii_if.phy_id = i;return 1;}}}return 0;
}/** Show the status of the media. Similar to mii_check_media however it* correctly shows the link speed for all (tested) pcnet32 variants.* Devices with no mii just report link state without speed.** Caller is assumed to hold and release the lp->lock.*////\额。。。判断curr_link那块让人看不懂,
static void pcnet32_check_media(struct net_device *dev, int verbose)
{struct pcnet32_private *lp = netdev_priv(dev);int curr_link;int prev_link = netif_carrier_ok(dev) ? 1 : 0;u32 bcr9;if (lp->mii) {curr_link = mii_link_ok(&lp->mii_if);} else {ulong ioaddr = dev->base_addr; /* card base I/O address */curr_link = (lp->a.read_bcr(ioaddr, 4) != 0xc0);//BCR4是网卡指示灯的状态。}if (!curr_link) {//链接已断if (prev_link || verbose) {netif_carrier_off(dev);//这将触发一个消息,通知上层链路已断,该功能是通过workqueue完成的。if (netif_msg_link(lp))printk(KERN_INFO "%s: link down\n", dev->name);}if (lp->phycount > 1) {curr_link = pcnet32_check_otherphy(dev);prev_link = 0;}} else if (verbose || !prev_link) {netif_carrier_on(dev);if (lp->mii) {if (netif_msg_link(lp)) {struct ethtool_cmd ecmd;mii_ethtool_gset(&lp->mii_if, &ecmd);printk(KERN_INFO"%s: link up, %sMbps, %s-duplex\n",dev->name,(ecmd.speed == SPEED_100) ? "100" : "10",(ecmd.duplex ==DUPLEX_FULL) ? "full" : "half");}bcr9 = lp->a.read_bcr(dev->base_addr, 9);if ((bcr9 & (1 << 0)) != lp->mii_if.full_duplex) {if (lp->mii_if.full_duplex)bcr9 |= (1 << 0);elsebcr9 &= ~(1 << 0);lp->a.write_bcr(dev->base_addr, 9, bcr9);}} else {if (netif_msg_link(lp))printk(KERN_INFO "%s: link up\n", dev->name);}}
}/** Check for loss of link and link establishment.* Can not use mii_check_media because it does nothing if mode is forced.*/
static void pcnet32_watchdog(struct net_device *dev)
{struct pcnet32_private *lp = netdev_priv(dev);unsigned long flags;/* Print the link status if it has changed */spin_lock_irqsave(&lp->lock, flags);///下面只是打印信息pcnet32_check_media(dev, 0);spin_unlock_irqrestore(&lp->lock, flags);////重新启动计时器mod_timer(&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
}///suspend,推测是系统为了省电,在不需要网络传输的时候断电
static int pcnet32_pm_suspend(struct pci_dev *pdev, pm_message_t state)
{struct net_device *dev = pci_get_drvdata(pdev);if (netif_running(dev)) {///Mark device as removed from system and therefore no longer available.netif_device_detach(dev);///close the controllerpcnet32_close(dev);}///pci_save_state - save the PCI configuration space of a device before suspendingpci_save_state(pdev);///pci_choose_state - Returns PCI power state suitable for given device and given system///pci_set_power_state - Set the power state of a PCI devicepci_set_power_state(pdev, pci_choose_state(pdev, state));return 0;
}static int pcnet32_pm_resume(struct pci_dev *pdev)
{struct net_device *dev = pci_get_drvdata(pdev);pci_set_power_state(pdev, PCI_D0);///pci_restore_state - Restore the saved state of a PCI devicepci_restore_state(pdev);if (netif_running(dev)) {pcnet32_open(dev);///Mark device as attached from system and restart if needed.netif_device_attach(dev);}return 0;
}///释放该设备申请的所有资源,unregister the net_device
static void __devexit pcnet32_remove_one(struct pci_dev *pdev)
{struct net_device *dev = pci_get_drvdata(pdev);if (dev) {struct pcnet32_private *lp = netdev_priv(dev);unregister_netdev(dev);pcnet32_free_ring(dev);release_region(dev->base_addr, PCNET32_TOTAL_SIZE);pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block),lp->init_block, lp->init_dma_addr);free_netdev(dev);pci_disable_device(pdev);pci_set_drvdata(pdev, NULL);}
}static struct pci_driver pcnet32_driver = {.name = DRV_NAME,.probe = pcnet32_probe_pci,.remove = __devexit_p(pcnet32_remove_one),.id_table = pcnet32_pci_tbl,///id_table是一个结构体数组,用来存放驱动程序适用的设备信息.suspend = pcnet32_pm_suspend,.resume = pcnet32_pm_resume,
};/* An additional parameter that may be passed in... */
static int debug = -1;
static int tx_start_pt = -1;
static int pcnet32_have_pci;module_param(debug, int, 0);
MODULE_PARM_DESC(debug, DRV_NAME " debug level");
module_param(max_interrupt_work, int, 0);
MODULE_PARM_DESC(max_interrupt_work,DRV_NAME " maximum events handled per interrupt");
module_param(rx_copybreak, int, 0);
MODULE_PARM_DESC(rx_copybreak,DRV_NAME " copy breakpoint for copy-only-tiny-frames");
module_param(tx_start_pt, int, 0);
MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)");
module_param(pcnet32vlb, int, 0);
MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)");
module_param_array(options, int, NULL, 0);
MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)");
module_param_array(full_duplex, int, NULL, 0);
MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)");
/* Module Parameter for HomePNA cards added by Patrick Simmons, 2004 */
module_param_array(homepna, int, NULL, 0);
MODULE_PARM_DESC(homepna,DRV_NAME" mode for 79C978 cards (1 for HomePNA, 0 for Ethernet, default Ethernet");MODULE_AUTHOR("Thomas Bogendoerfer");
MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards");
MODULE_LICENSE("GPL");#define PCNET32_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
/*
2897 NETIF_MSG_DRV = 0x0001,
2898 NETIF_MSG_PROBE = 0x0002,
2899 NETIF_MSG_LINK = 0x0004,
2900 NETIF_MSG_TIMER = 0x0008,
2901 NETIF_MSG_IFDOWN = 0x0010,
2902 NETIF_MSG_IFUP = 0x0020,
2903 NETIF_MSG_RX_ERR = 0x0040,
2904 NETIF_MSG_TX_ERR = 0x0080,
2905 NETIF_MSG_TX_QUEUED = 0x0100,
2906 NETIF_MSG_INTR = 0x0200,
2907 NETIF_MSG_TX_DONE = 0x0400,
2908 NETIF_MSG_RX_STATUS = 0x0800,
2909 NETIF_MSG_PKTDATA = 0x1000,
2910 NETIF_MSG_HW = 0x2000,
2911 NETIF_MSG_WOL = 0x4000,
*/
/// PCNET32_MSG_DEFAULT = 0111
static int __init pcnet32_init_module(void)
{
printk(KERN_INFO "%s", version);/// 如果没有设置debug,则使用默认的debug level/****** pcnet32_dubug 和debug都是int型变量,表示消息级别,其中的某些使用的位,每一位对* 应是否打印一种消息,在需要决定是否打印某种消息时,如果该位为1则打印,为0则不答应.* netif_msg_init(debug, PCNET32_MSG_DEFAULT)是来初始化pcnet32_debug,* 如果传入的debug小于0或者大于sizeof(u32)*8则返回PCNET32_MSG_DEFAULT(即默认的debug level),* 如果debug = 0,返回0,其他情况返回 ( 1 << debug_value - 1 )*/pcnet32_debug = netif_msg_init(debug, PCNET32_MSG_DEFAULT);
////???????if ((tx_start_pt >= 0) && (tx_start_pt <= 3))tx_start = tx_start_pt;/* find the PCI devices *////注册PCI驱动,成功返回0,失败返回负值,pcnet32_have_pci表示当前系统存在PCI子系统(不一定存在驱动支持的网卡设备,因为注册驱动时是可以没有网卡设备的)if (!pci_register_driver(&pcnet32_driver))pcnet32_have_pci = 1;/* should we find any remaining VLbus devices ? *///// 不用if (pcnet32vlb)pcnet32_probe_vlbus(pcnet32_portlist);/* * cards_found定义是static int的全局变量(因为是static所以初始值为0,用来表示本驱 * 动探测到的网卡数量 (即本驱动控制的网卡的数量)* pcnet32_probe_pci中调用的探测函数pcnet32_probe1,每完成对一个NIC的探测,都会* 执行cards_found++ ,每次插入一个设备,如果PCI子系统发现网卡是本驱动支持的网卡* 型号,就会调用一次pcnet32_probe_pci* 下面的语句功能:探测到网卡,并且debug level设置了NET_IF_MSG_PROBE(探测消息)则打印cards found 消息*/if (cards_found && (pcnet32_debug & NETIF_MSG_PROBE))printk(KERN_INFO PFX "%d cards_found.\n", cards_found);return (pcnet32_have_pci + cards_found) ? 0 : -ENODEV;
}////释放所有已经获得的资源,注销驱动
///资源主要是各种内存缓冲区,还有把驱动unregist
///因为单网卡,所以while循环是不需要的
static void __exit pcnet32_cleanup_module(void)
{struct net_device *next_dev;while (pcnet32_dev) {struct pcnet32_private *lp = netdev_priv(pcnet32_dev);next_dev = lp->next;unregister_netdev(pcnet32_dev);pcnet32_free_ring(pcnet32_dev);release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block),lp->init_block, lp->init_dma_addr);free_netdev(pcnet32_dev);pcnet32_dev = next_dev;}if (pcnet32_have_pci)pci_unregister_driver(&pcnet32_driver);
}module_init(pcnet32_init_module);
module_exit(pcnet32_cleanup_module);/** Local variables:* c-indent-level: 4* tab-width: 8* End:*/
Linux网卡驱动pcnet32.c的注释(AM79C9*系列网卡的驱动)相关推荐
- 如何在Linux系统下安装英特尔® Arc™系列独立显卡驱动以及进行AI推理性能测试...
点击蓝字 关注我们,让开发变得更有趣 作者 | 周兆靖 排版 | 李擎 如何在Linux系统下安装英特尔® Arc™系列 独立显卡驱动以及进行AI推理性能测试 目录 1.本文目的 2.Linux 系统 ...
- i217lm网卡驱动linux,Intel英特尔I217/I218/I219系列网卡驱动
Intel英特尔I217/I218/I219系列网卡驱动官方版是一款专业的驱动软件.Intel英特尔I217/I218/I219系列网卡驱动最新版该驱动仅支持I217/I218系列网卡.除此之外Int ...
- 联想服务器linux系统raid驱动,ThinkSystem服务器RAID 530/930系列阵列卡驱动及安装RHEL7.3要点说明...
知识点分析: 本文介绍在配置为RAID 530/930系列阵列卡的 ThinkSystem ST550 ThinkSystem ST558 ThinkSystem SR530 ThinkSystem ...
- 7080mt安装linux网卡驱动,Intel英特尔PRO100/1000/10GbE系列网卡驱动
Intel英特尔PRO100/1000/10GbE系列网卡驱动17.3版For WinXP/2003-32/2003-64/Vista-32/Vista-64/Win7-32/Win7-64/Win8 ...
- linux网卡驱动离线安装_linux系统如何安装网卡驱动
linux系统如何安装网卡驱动 很多初学者都觉得能够在自己的笔记本上安装linux系统是一件很酷的事情,结果两个小时安装好linux系统,却发现缺少各种驱动, 为了让各位少走一些弯路,小编在这里把li ...
- 【Linux】ubuntu16.04/18.04安装I219-V有线网卡驱动
intel219网卡的linux驱动,ubuntu16.04/18.04安装I219-V有线网卡驱动问题解决方法(示例代码) 安装ubuntu16.04/18.04 LTS后无网络连接的解决方法: 1 ...
- 在linux(ubuntu16.04)系统上安装RTL8822CE网卡驱动
在linux(ubuntu16.04)系统上安装RTL8822CE网卡驱动 1.下载8822CE驱动https://github.com/alanfox2000/realtek-linux/tree/ ...
- w311m linux驱动下载,腾达(Tenda)W311M V3.0网卡驱动(LINUX)
这是腾达(Tenda)W311M V3.0网卡驱动(LINUX)下载,支持Soft AP功能. 软件说明 支持Soft AP功能. 硬件介绍 W311M是一款采用11N无线技术,无线传输速率达150M ...
- 创锐讯网卡 linux,高通创锐讯Atheros AR813X/AR815X/AR816X系列网卡驱动
高通创锐讯Atheros AR813X/AR815X/AR816X系列网卡驱动,支持型号众多,ATHEROS在无线网卡界有口皆碑,用户很多,这里的驱动相信很多人也是需要安装的 驱动说明 这是Ather ...
最新文章
- AttributeError: module ‘seaborn‘ has no attribute ‘tsplot‘
- R使用glm构建logistic回归模型
- 学文科的优势_男生学文科,出路在哪里?带了十几年文科的班主任道出了实情...
- 终于装上了office2010
- 利用图基Tukey method检测数据集中的异常值
- ubuntu16.04安装,使用redis布隆过滤器示例
- Android 7.0 Nougat介绍
- 75-商品服务-品牌分类关联与级联更新
- MAVEN 私有仓库库迁移
- windows端口备忘
- 亚马逊因密码泄露重置部分用户密码
- wamp php 升级,wamp升级PHP7.1
- html没有内容怎么爬,Url没有在网页中返回正确的html(对于我的Java爬虫)
- winform datagridview 自定义tooltip
- 亲,你们都在家办公吗?啥感受?hahaha
- Java链表——插入和删除
- 软件开发安全左移最佳工具-iast
- 代码从svn到工作空间,Myeclipse中java项目转成Web项目
- 2020年G1工业锅炉司炉考试总结及G1工业锅炉司炉试题及答案
- 分销商城小程序系统怎么选择?