您的位置:首页 > 理论基础 > 计算机网络

Linux内核--网络栈实现分析(十一)--驱动程序层(下)

2013-04-13 13:33 501 查看
本文分析基于Linux Kernel 1.2.13

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7555870

更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明

注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。

在博文Linux内核--网络栈实现分析(三)--驱动程序层(链路层)(上)中对网络设备结构,网络设备初始化等函数有了初步认识,并列出了设备的发送和接收函数。

设备接口层会调用函数设备驱动层ei_start_xmit()函数发送数据,这里没有详细分析。

[cpp] view
plaincopy

static int ei_start_xmit(struct sk_buff *skb, struct device *dev)  

{  

    int e8390_base = dev->base_addr;  

    struct ei_device *ei_local = (struct ei_device *) dev->priv;//取出网卡设备的私有数据,和具体的网卡型号有关,在ethdev_init()函数中已经分配空间  

    int length, send_length;  

    unsigned long flags;  

      

/* 

 *  We normally shouldn't be called if dev->tbusy is set, but the 

 *  existing code does anyway. If it has been too long since the 

 *  last Tx, we assume the board has died and kick it. 

 */  

   

    if (dev->tbusy) {    /* Do timeouts, just like the 8003 driver. */  

        ........................................  

        ........................................  

    }  

      

    /* Sending a NULL skb means some higher layer thinks we've missed an 

       tx-done interrupt. Caution: dev_tint() handles the cli()/sti() 

       itself. */  

    if (skb == NULL) {//该条件似乎不会发生,这用于处理内核中的BUG  

        dev_tint(dev);//发送设备中的所有缓存的数据包  

        return 0;  

    }  

      

    length = skb->len;  

    if (skb->len <= 0)  

        return 0;  

  

    save_flags(flags);  

    cli();  

  

    /* Block a timer-based transmit from overlapping. */  

    if ((set_bit(0, (void*)&dev->tbusy) != 0) || ei_local->irqlock) {  

    printk("%s: Tx access conflict. irq=%d lock=%d tx1=%d tx2=%d last=%d\n",  

        dev->name, dev->interrupt, ei_local->irqlock, ei_local->tx1,  

        ei_local->tx2, ei_local->lasttx);  

    restore_flags(flags);  

    return 1;  

    }  

  

    /* Mask interrupts from the ethercard. */  

    outb(0x00, e8390_base + EN0_IMR);  

    ei_local->irqlock = 1;  

    restore_flags(flags);  

  

    send_length = ETH_ZLEN < length ? length : ETH_ZLEN;  

  

    if (ei_local->pingpong) {  

        int output_page;  

        if (ei_local->tx1 == 0) {  

            output_page = ei_local->tx_start_page;  

            ei_local->tx1 = send_length;  

            if (ei_debug  &&  ei_local->tx2 > 0)  

                printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",  

                       dev->name, ei_local->tx2, ei_local->lasttx,  

                       ei_local->txing);  

        } else if (ei_local->tx2 == 0) {  

            output_page = ei_local->tx_start_page + 6;  

            ei_local->tx2 = send_length;  

            if (ei_debug  &&  ei_local->tx1 > 0)  

                printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",  

                       dev->name, ei_local->tx1, ei_local->lasttx,  

                       ei_local->txing);  

        } else {    /* We should never get here. */  

            if (ei_debug)  

                printk("%s: No Tx buffers free. irq=%d tx1=%d tx2=%d last=%d\n",  

                    dev->name, dev->interrupt, ei_local->tx1,   

                    ei_local->tx2, ei_local->lasttx);  

            ei_local->irqlock = 0;  

            dev->tbusy = 1;  

            outb_p(ENISR_ALL, e8390_base + EN0_IMR);  

            return 1;  

        }  

        ei_block_output(dev, length, skb->data, output_page);  

        if (! ei_local->txing) {  

            ei_local->txing = 1;  

            NS8390_trigger_send(dev, send_length, output_page);  

            dev->trans_start = jiffies;  

            if (output_page == ei_local->tx_start_page)  

                ei_local->tx1 = -1, ei_local->lasttx = -1;  

            else  

                ei_local->tx2 = -1, ei_local->lasttx = -2;  

        } else  

            ei_local->txqueue++;  

  

        dev->tbusy = (ei_local->tx1  &&  ei_local->tx2);  

    } else {  /* No pingpong, just a single Tx buffer. */  

        ei_block_output(dev, length, skb->data, ei_local->tx_start_page);  

        ei_local->txing = 1;  

        NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);  

        dev->trans_start = jiffies;  

        dev->tbusy = 1;  

    }  

      

    /* Turn 8390 interrupts back on. */  

    ei_local->irqlock = 0;  

    outb_p(ENISR_ALL, e8390_base + EN0_IMR);  

  

    dev_kfree_skb (skb, FREE_WRITE);  

      

    return 0;  

其中的dev_tint()函数是将设备的所有缓存队列中的数据全部调用dev_queue_xmit()发送全部数据包。

[cpp] view
plaincopy

/* 

 *  This routine is called when an device driver (i.e. an 

 *  interface) is ready to transmit a packet. 

 */  

//该函数功能:遍历设备的缓冲队列,对所有的数据包调用dev_queue_xmit()函数发送数据  

void dev_tint(struct device *dev)  

{  

    int i;  

    struct sk_buff *skb;  

    unsigned long flags;  

      

    save_flags(flags);    

    /* 

     *  Work the queues in priority order 

     */  

       

    for(i = 0;i < DEV_NUMBUFFS; i++)   

    {  

        /* 

         *  Pull packets from the queue 

         */  

           

  

        cli();  

        while((skb=skb_dequeue(&dev->buffs[i]))!=NULL)  

        {  

            /* 

             *  Stop anyone freeing the buffer while we retransmit it 

             */  

            skb_device_lock(skb);  

            restore_flags(flags);  

            /* 

             *  Feed them to the output stage and if it fails 

             *  indicate they re-queue at the front. 

             */  

            dev_queue_xmit(skb,dev,-i - 1);//注意优先级的计算方式,在函数dev_queue_xmit()中优先级若<0则计算pri=-pri-1=-(-i-1)-1=i,  

                               //这样做的目的就是为了得到正确的where值,函数(dev_queue_xmit())中  

            /* 

             *  If we can take no more then stop here. 

             */  

            if (dev->tbusy)  

                return;  

            cli();  

        }  

    }  

    restore_flags(flags);  

}  



驱动层严格的说不属于内核网络栈的内容,和硬件关系密切,何况这种网卡硬件设备可能已经不用了,这里就没有详细分析,如果对网卡驱动有兴趣可以看一下之前的分析的ARM-Linux下的DM9000网卡驱动的分析,链接如下:

ARM-Linux驱动--DM9000网卡驱动分析(一)

ARM-Linux驱动--DM9000网卡驱动分析(二)

ARM-Linux驱动--DM9000网卡驱动分析(三)

ARM-Linux驱动--DM9000网卡驱动分析(四)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐