很久之前读的网卡驱动源码,很多东西已经忘记了,最近面试被问道了网卡收数据包的全过程,只能答出一个很简单的过程,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*系列网卡的驱动)相关推荐

  1. 如何在Linux系统下安装英特尔® Arc™系列独立显卡驱动以及进行AI推理性能测试...

    点击蓝字 关注我们,让开发变得更有趣 作者 | 周兆靖 排版 | 李擎 如何在Linux系统下安装英特尔® Arc™系列 独立显卡驱动以及进行AI推理性能测试 目录 1.本文目的 2.Linux 系统 ...

  2. i217lm网卡驱动linux,Intel英特尔I217/I218/I219系列网卡驱动

    Intel英特尔I217/I218/I219系列网卡驱动官方版是一款专业的驱动软件.Intel英特尔I217/I218/I219系列网卡驱动最新版该驱动仅支持I217/I218系列网卡.除此之外Int ...

  3. 联想服务器linux系统raid驱动,ThinkSystem服务器RAID 530/930系列阵列卡驱动及安装RHEL7.3要点说明...

    知识点分析: 本文介绍在配置为RAID 530/930系列阵列卡的 ThinkSystem ST550 ThinkSystem ST558 ThinkSystem SR530 ThinkSystem ...

  4. 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 ...

  5. linux网卡驱动离线安装_linux系统如何安装网卡驱动

    linux系统如何安装网卡驱动 很多初学者都觉得能够在自己的笔记本上安装linux系统是一件很酷的事情,结果两个小时安装好linux系统,却发现缺少各种驱动, 为了让各位少走一些弯路,小编在这里把li ...

  6. 【Linux】ubuntu16.04/18.04安装I219-V有线网卡驱动

    intel219网卡的linux驱动,ubuntu16.04/18.04安装I219-V有线网卡驱动问题解决方法(示例代码) 安装ubuntu16.04/18.04 LTS后无网络连接的解决方法: 1 ...

  7. 在linux(ubuntu16.04)系统上安装RTL8822CE网卡驱动

    在linux(ubuntu16.04)系统上安装RTL8822CE网卡驱动 1.下载8822CE驱动https://github.com/alanfox2000/realtek-linux/tree/ ...

  8. w311m linux驱动下载,腾达(Tenda)W311M V3.0网卡驱动(LINUX)

    这是腾达(Tenda)W311M V3.0网卡驱动(LINUX)下载,支持Soft AP功能. 软件说明 支持Soft AP功能. 硬件介绍 W311M是一款采用11N无线技术,无线传输速率达150M ...

  9. 创锐讯网卡 linux,高通创锐讯Atheros AR813X/AR815X/AR816X系列网卡驱动

    高通创锐讯Atheros AR813X/AR815X/AR816X系列网卡驱动,支持型号众多,ATHEROS在无线网卡界有口皆碑,用户很多,这里的驱动相信很多人也是需要安装的 驱动说明 这是Ather ...

最新文章

  1. AttributeError: module ‘seaborn‘ has no attribute ‘tsplot‘
  2. R使用glm构建logistic回归模型
  3. 学文科的优势_男生学文科,出路在哪里?带了十几年文科的班主任道出了实情...
  4. 终于装上了office2010
  5. 利用图基Tukey method检测数据集中的异常值
  6. ubuntu16.04安装,使用redis布隆过滤器示例
  7. Android 7.0 Nougat介绍
  8. 75-商品服务-品牌分类关联与级联更新
  9. MAVEN 私有仓库库迁移
  10. windows端口备忘
  11. 亚马逊因密码泄露重置部分用户密码
  12. wamp php 升级,wamp升级PHP7.1
  13. html没有内容怎么爬,Url没有在网页中返回正确的html(对于我的Java爬虫)
  14. winform datagridview 自定义tooltip
  15. 亲,你们都在家办公吗?啥感受?hahaha
  16. Java链表——插入和删除
  17. 软件开发安全左移最佳工具-iast
  18. 代码从svn到工作空间,Myeclipse中java项目转成Web项目
  19. 2020年G1工业锅炉司炉考试总结及G1工业锅炉司炉试题及答案
  20. 分销商城小程序系统怎么选择?

热门文章

  1. SAP FICO 第五节 物料分类账ML配置及应用
  2. Macbook Pro 外接显示器后,鼠标滑动延迟
  3. 正则表达式—————根据银行卡号判断银行名称
  4. 计算机专业毕设太难做不出来怎么办?
  5. vue 二级路由嵌套和二级路由高亮问题
  6. 华为鸿蒙系统基于安卓系统方面有哪些提升
  7. 大学专业有C语言专业吗,大学里哪些专业开设C语言、数电、模电、单片机、嵌入式等课程?...
  8. 记一次docker进不去容器的经历
  9. Chrome的一些快捷键
  10. 2k 幻14_华硕幻14(2060 2k版)值得入手吗?