Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1911818
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: LINUX

2012-02-07 14:54:20

一个网络设备,在其驱动程序中用一个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记录缓冲区数组的首地址。
    其它几个成员,在用到时再具体描述。
阅读(1633) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~