Linux网卡驱动pcnet32.c的注释(AM79C9*系列网卡的驱动)

本文档详细注释了Linux内核中的pcnet32.c驱动,主要关注AM79C970A网卡。通过复习,作者重新介绍了网卡接收数据包的完整过程,包括NAPI机制,旨在帮助读者理解数据链路层的驱动实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很久之前读的网卡驱动源码,很多东西已经忘记了,最近面试被问道了网卡收数据包的全过程,只能答出一个很简单的过程,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 the
            Director, National Security Agency.
 *
            This software may be used and distributed according to the terms
            of 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_NAPI
    struct pcnet32_private *lp = netdev_priv(dev);
#endif
    dev->trans_start = jiffies;
#ifdef CONFIG_PCNET32_NAPI
    napi_disable(&lp->napi);
#endif
    netif_tx_disable(dev);
}

static void pcnet32_netif_start(struct net_device *dev)
{
#ifdef CONFIG_PCNET32_NAPI
    struct pcnet32_private *lp = netdev_priv(dev);
    ulong ioaddr = dev->base_addr;
    u16 val;
#endif
    netif_wake_queue(dev);
#ifdef CONFIG_PCNET32_NAPI
    val = 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的中断,指的应该是全关,而不是仅关掉一个吧
 * 第二步:进行中断处理(这个不是中断服务程序吧??
 * 第三部:中断处理结束后,开中断
 */
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值