一个网络设备,在其驱动程序中用一个net_device结构描述,该结构有一个重要的成员priv,它是一个指针,指向驱动程序自己定义的私有数据。不同的网络设备驱动程序,其私有数据各不相同。下面是PCNET32驱动程序的私有数据:
struct pcnet32_private {
struct pcnet32_rx_head rx_ring[RX_RING_SIZE];
struct pcnet32_tx_head tx_ring[TX_RING_SIZE];
struct pcnet32_init_block init_block;
dma_addr_t dma_addr;
struct pci_dev *pci_dev;
const char *name;
struct sk_buff *tx_skbuff[TX_RING_SIZE];
struct sk_buff *rx_skbuff[RX_RING_SIZE];
dma_addr_t tx_dma_addr[TX_RING_SIZE];
dma_addr_t rx_dma_addr[RX_RING_SIZE];
struct pcnet32_access a;
spinlock_t lock;
unsigned int cur_rx, cur_tx;
unsigned int dirty_rx, dirty_tx;
struct net_device_stats stats;
char tx_full;
int options;
unsigned int dxsuflo:1,
mii:1;
struct net_device *next;
struct mii_if_info mii_if;
struct timer_list watchdog_timer;
struct timer_list blink_timer;
};
下面逐个分析该结构的重要成员。
tx_skbuff和rx_skbuff。这是两个sk_buff的数组,其长度分别为TX_RING_SIZE(16)和RX_RING_SIZE
(32)。sk_buff是一个套接字缓冲区,在Linux内核中处于网络子系统的核心地位,由它保存发送和接收的数据,关于该结构的详细描述参阅
《Linux设备驱动程序》第三版512页。其中tx_skbuff是发送缓冲区数组,总共有16个,而rx_skbuff是接收缓冲区数组,总共有32
项。
tx_dma_addr和rx_dma_addr。对于分配好的sk_buff缓冲区,我们都要把它们映射成为DMA缓冲区,这两个数组用于记录相应的DMA缓冲区的地址。
rx_ring和tx_ring。这是pcnet32_rx_head和pcnet32_tx_head的结构体,它们用于记录对应的sk_buff的当前状态,可以称为描述符。这两个结构体的定义如下:
struct pcnet32_rx_head {
u32 base;
s16 buf_length;
s16 status;
u32 msg_length;
u32 reserved;
};
struct pcnet32_tx_head {
u32 base;
s16 length;
s16 status;
u32 misc;
u32 reserved;
};
对于tx_skbuff和rx_skbuff数组,在使用逻辑上是呈环形的,即用完最后1个后,重新使用第1个,所以,在源代码中会看到很多的ring命名。下面是对rx ring和tx ring的初始化函数:
static int pcnet32_init_ring(struct net_device *dev)
{
struct pcnet32_private *lp = dev->priv;
int i;
lp->tx_full = 0;
lp->cur_rx = lp->cur_tx = 0;
lp->dirty_rx = lp->dirty_tx = 0;
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *rx_skbuff = lp->rx_skbuff[i];
if (rx_skbuff == NULL) {
if (!(rx_skbuff = lp->rx_skbuff[i] = dev_alloc_skb (PKT_BUF_SZ))) {
printk(KERN_ERR "%s: pcnet32_init_ring dev_alloc_skb failed.\n",
dev->name);
return -1;
}
skb_reserve (rx_skbuff, 2);
}
rmb();
if (lp->rx_dma_addr[i] == 0)
lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->data,
PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]);
lp->rx_ring[i].buf_length = le16_to_cpu(2-PKT_BUF_SZ);
wmb(); /* Make sure owner changes after all others are visible */
lp->rx_ring[i].status = le16_to_cpu(0x8000);
}
for (i = 0; i < 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;
}
lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
for (i = 0; i < 6; i++)
lp->init_block.phys_addr[i] = dev->dev_addr[i];
lp->init_block.rx_ring = (u32)le32_to_cpu(lp->dma_addr +
offsetof(struct pcnet32_private, rx_ring));
lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr +
offsetof(struct pcnet32_private, tx_ring));
wmb(); /* Make sure all changes are visible */
return 0;
}
该初始化函数中,对接收缓冲区数组,都是预先分配好,并置好了描述符的相应状态。而对于发送缓冲区,并没有分配,只是初始化了描述符,等到使用时再进行分配。
该初始化函数中,还有一些pcnet32_private中的成员,我们还未描述到,下面一一描述。
cur_rx,cur_tx,dirty_rx和dirty_tx。这是四个关于套接字缓冲区的统计变量,记录当前缓冲区的使用情况。
init_block。这是一个pcnet32_init_block的结构,该结构定义如下:
struct pcnet32_init_block {
u16 mode;
u16 tlen_rlen;
u8 phys_addr[6];
u16 reserved;
u32 filter[2];
/* Receive and transmit ring base, along with extra bits. */
u32 rx_ring;
u32 tx_ring;
};
phys_addr记录mac地址,rx_ring和tx_ring记录缓冲区数组的首地址。
其它几个成员,在用到时再具体描述。
阅读(1626) | 评论(0) | 转发(0) |