很久之前读的网卡驱动源码,很多东西已经忘记了,最近面试被问道了网卡收数据包的全过程,只能答出一个很简单的过程,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的中断,指的应该是全关,而不是仅关掉一个吧
* 第二步:进行中断处理(这个不是中断服务程序吧??
* 第三部:中断处理结束后,开中断
*/