《Linux設備驅動開發詳解》——第16章 Linux網絡設備驅動二

16.4 網絡設備的打開與釋放

      網絡設備打開:

              使能設備使用的硬件資源,申請I/O區域,中斷和DMA通道等。

              調用Linux內核提供的netif_start_queue()函數,激活設備發送隊列。

       網絡設備關閉:

              調用Linux內核提供的netif_stop_queue()函數,停止設備傳輸包。

              釋放設備所使用的I/O區域,中斷和DMA資源。

<pre name="code" class="cpp">
/* 網絡設備打開和釋放函數模板 */
int xxx_open(struct net_device *dev)
{
     /* 申請端口,IRQ等,類似於fops->open */
      ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);
     ...
      netif_start_queue(dev);
     ...
}


<pre name="code" class="plain">
int xxx_release(struct net_device *dev)
{
     /* 釋放端口,IRQ等,類似於 fops->close */
     free_irq(dev->irq, dev);
     ...
     netif_stop_queue(dev);    /* can't transmit any more */
     ...
}

16.5 數據發送流程

        Linux網絡子系統在發送數據包時,會調用驅動程序提供的hard_start_transmit()函數,該函數用於啓動數據包的發送。

        網絡設備驅動完成數據包發送的流程如下:

      (1)網絡設備驅動程序從上層協議傳遞過來的sk_buff參數獲得數據包的有效數據和長度,將有效數據放入臨時緩衝區。  

      (2)對於以太網,如果有效數據的長度小於以太網衝突檢測所要求數據幀的最小長度ETH_ZLEN,則臨時緩衝區的末尾填充0.

      (3)設置硬件的寄存器,驅動網絡設備進行數據發送操作。


/* 網絡設備驅動程序的數據包發送函數模板 */
int xxx_tx(struct sk_buff *skb, struct net_device *dev)
{
   int len;
   char *data , shortpkt[ETH_ZLEN]; 
   if( xxx_send_available(...) ){ /* 發送隊列未滿,可以發送 */
         /* 獲得有效數據指針和長度 */    
         data = skb->data;
         len = skb->len;
         if( len < ETH_ZLEN ){
              /* 如果幀長小於以太網最小長度,補0 */
              memset(shortpkt, 0, ETH_ZLEN);
              len = ETH_ZLEN;
              data = shortpkt;
         } 
         dev->trans_start = jiffies;     /* 記錄發送實踐戳 */
         /* 設置硬件寄存器讓硬件把數據包發送出去 */
         xxx_hw_tx(data, len, dev);
         ...
   }else{
        netif_stop_queue(dev); //當發送隊列爲滿或因其他原因來不及發送當前上層傳下來的包,則調用次函數阻止上層繼續向網絡設備驅動傳輸數據包
        ...                    //當忙於發送的數據包被髮送完成後,TX結束的中斷處理中,應該調用netif_wake_queue()喚醒被阻塞的上層,以啓動它繼續向網絡設備驅動傳輸數據包
   }
}
    當數據傳輸超時時,意味着當前的發送操作失敗或硬件已陷入未知狀態,此時,數據包發送超時處理函數xxx_tx_timeout()將被調用,這個函數也需要調用Linux內核提供的netif_wake_queue()函數重啓設備發送隊列。

       /* 網絡設備驅動程序的數據包發送超時函數 */
       void xxx_tx_timeout(struct net_device *dev)
       {
           ...
           netif_wake_queue(dev); //重啓設備發送隊列
       }
      從上文可知,netif_wake_queue() 和 netif_stop_queue() 是數據發送流程中要調用的兩個非常重要的函數,分別用於喚醒和阻止上層向下傳送數據包,它們的原型定義在 include/linux/netdevice.h,如下:

              static inline void netif_wake_queue(struct net_device *dev):

              static inline void netif_stop_queue(struct net_device *dev):

16.6 數據接收流程

        網絡設備接收數據的主要方式是由中斷引發設備的中斷處理函數。中斷處理函數判斷中斷類型,如果爲接收中斷,則讀取接收到的數據,分配sk_buff數據結構和數據緩衝區,將接收到的數據複製到數據緩衝區,並調用netif_rx()將sk_buff傳遞給上層協議。


/* 網絡設備驅動的中斷處理函數 */
static void xxx_interrupt(int irq, void *dev_id)
{
        ...
        switch(status & ISQ_EVENT_MASK){
             case ISQ_RECEIVER_EVENT:
                  /* 獲取數據包 */
                  xxx_rx(dev);
                  break;
                  /* 其他類型的中斷 */
       }
}
<pre name="code" class="cpp">
static void xxx_rx(struct xxx_device *dev)
{
        ...
        length = get_rev_len(...);
        /* 分配新的套接字緩衝區 */
        skb = dev_alloc_skb(length + 2);

        skb_reserve(skb, 2);  //對齊
        skb->dev = dev;
        /* 讀取硬件上接收到的數據 */、
        insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1);
        if( length & 1 )
            skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);

        /* 獲取上層協議類型 */
        skb->protocol = eth_type_trans(skb, dev);

       /* 把數據包遞交給上層 */
       netif_rx(skb);

       /* 記錄接收實踐戳 */
       dev->last_rx = jiffies;
       ...
}

     如果是NAPI兼容的設備驅動,則可以通過poll方式接收數據包,這種情況下,我們需要爲設備驅動提供作爲netif_napi_add()參數的xxx_poll()函數














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章