linux协议栈之链路层上的数据传输-----从网卡驱动说起(二)

出处:http://ericxiao.cublog.cn/

------------------------------------------

它主要完成:对网应对应的net net_device赋初值。并向内核调用register_netdev完成网络设备的注册,网络设备注册我们在上一节中已经说过,这里不再赘述。

看一下net_device中几个关键的函数:

//在设备将打开的时候,调用此函数

netdev->open = e100_open;

//在设备停用的时候调用此函数

netdev->stop = e100_close;

//设备发送数据的时候调用此函数

netdev->hard_start_xmit = e100_xmit_frame;

到此时,网卡的初始化工作已经完成了。之后就可以操作网卡了。

那网卡应该怎么使用呢?必须首先唤起网卡,即使之UP,例如 ifconfig eth0 up

此时,内核会根据接口名字“eth0”找到对应的net_device.然后调用 net_device-> open.即:e100_open

分析如下:

static int e100_open(struct net_device *netdev)

{

         struct nic *nic = netdev_priv(netdev);

         int err = 0;

 

         //网卡正在UP,关闭载波信号

         netif_carrier_off(netdev);

         if((err = e100_up(nic)))

                   DPRINTK(IFUP, ERR, "Cannot open interface, aborting./n");

         return err;

}

我们关心的是e100_up。跟踪如下:

static int e100_up(struct nic *nic)

{

         int err;

 

         //分配收包队列

         if((err = e100_rx_alloc_list(nic)))

                   return err;

         //分配控制队列

         if((err = e100_alloc_cbs(nic)))

                   goto err_rx_clean_list;

         //硬件初始化

         if((err = e100_hw_init(nic)))

                   goto err_clean_cbs;

         //多播

         e100_set_multicast_list(nic->netdev);

         //开始接收数据

         e100_start_receiver(nic);

         mod_timer(&nic->watchdog, jiffies);

         //注册中断例程

         if((err = request_irq(nic->pdev->irq, e100_intr, SA_SHIRQ,

                   nic->netdev->name, nic->netdev)))

                   goto err_no_irq;

         //启用中断

         e100_enable_irq(nic);

         netif_wake_queue(nic->netdev);

         return 0;

 

err_no_irq:

         del_timer_sync(&nic->watchdog);

err_clean_cbs:

         e100_clean_cbs(nic);

err_rx_clean_list:

         e100_rx_clean_list(nic);

         return err;

}

在此函数中,我们可以看到,它主要完成了:接立接收环形DMA缓冲区。注册了中断处理函数

关于环形DMA缓冲区接立是由e100_rx_alloc_list(nic)完成的

static int e100_rx_alloc_list(struct nic *nic)

{

         struct rx *rx;

         // nic->params.rfds.count,接收缓存的总个数

         unsigned int i, count = nic->params.rfds.count;

         //rx_to_use:正在存在数据的位置

         //rx_to_clean:数据的初始为止。所以。数据的有限位置是从rx_to_userx_to_use

         nic->rx_to_use = nic->rx_to_clean = NULL;

         if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC)))

                   return -ENOMEM;

         memset(nic->rxs, 0, sizeof(struct rx) * count);

         //遍历并建立循环链表

         for(rx = nic->rxs, i = 0; i < count; rx++, i++) {

                   rx->next = (i + 1 < count) ? rx + 1 : nic->rxs;

                   rx->prev = (i == 0) ? nic->rxs + count - 1 : rx - 1;

                   if(e100_rx_alloc_skb(nic, rx)) {

                            e100_rx_clean_list(nic);

                            return -ENOMEM;

                   }

         }

         //初始化起如位置为nic->rxs

         nic->rx_to_use = nic->rx_to_clean = nic->rxs;

 

         return 0;

}

为设备建立DMA映射的主函数为e100_rx_alloc_skb().分析如下:

static inline int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)

{

         unsigned int rx_offset = 2; /* u32 align protocol headers */

         if(!(rx->skb = dev_alloc_skb(RFD_BUF_LEN + rx_offset)))

                   return -ENOMEM;

         /* Align, init, and map the RFD. */

         rx->skb->dev = nic->netdev;

         //在数据存储区之前空出offset空间

skb_reserve(rx->skb, rx_offset);

//skb->data前部置RFD

         memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd));

         //DMA内存映射,映射至skb->data

         rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,

                   RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);

 

         /* Link the RFD to end of RFA by linking previous RFD to

l        this one, and clearing EL bit of previous.  */

//初始化前一个skb中的控制信息

         if(rx->prev->skb) {

                   struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data;

                   put_unaligned(cpu_to_le32(rx->dma_addr),

                            (u32 *)&prev_rfd->link);

                   wmb();

                   prev_rfd->command &= ~cpu_to_le16(cb_el);

                   pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr,

                            sizeof(struct rfd), PCI_DMA_TODEVICE);

         }

 

         return 0;

}

在这个函数里,主要完成了:DMA环形链表的建立。在这里涉及到了一个重要的数据结构sk_buff.稍后再给出它的结构分析。在这里我们只要知道在skb->data里储存的是接收数据就OK了。值得一提的是,Intel 100M 网卡对接收数据的处理,跟平时遇到的网卡不一样,接收数据时会由接收控制RU写入接收信息,由此判断接收是否完全等信息。也就是我们在代码里面看到的rfd.所以,在skb->data对应的就是rfd+网络传过来的数据.

到这里,接收准备工作已经完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值