天天看點

usbnet驅動深入分析-usb虛拟網卡host端【轉】

1、驅動流程:

usbnet驅動深入分析-usb虛拟網卡host端【轉】

2、明确probe函數的功能:

probe有usb core 經枚舉過程,比對 id_table ,識别到驅動,并調用probe來初始化一些資訊。

如 dev->driver_info = info 隻是取得注冊時的一些資訊,用于一些比較特殊的裝置,如bind 與收發與普通的網卡不同時,增加或減少一些配置。

主要做了:

 skb_queue_head_init (&dev->rxq);

 skb_queue_head_init (&dev->txq);

 skb_queue_head_init (&dev->done);

/*tasklet 軟中斷初始化,資料接收發送關鍵函數*/

 dev->bh.func = usbnet_bh;

 dev->bh.data = (unsigned long) dev;

/*初始化關鍵隊列,通過usbnet_defer_kevent 設定USB資料傳輸過程錯誤,并設定标志,調用schedule_work,送出到工作隊列來處理錯誤事件*/

 INIT_WORK (&dev->kevent, kevent);

/*核心定時器初始化,用來處理USB傳輸過程的一些錯誤,如一些CRC錯誤,調用mod_timer啟動核心定時器*/

 dev->delay.function = usbnet_bh;

 dev->delay.data = (unsigned long) dev;

 init_timer (&dev->delay);

初始化了三個隊列,分别是發送 接收 處理;

1)資料發送完成後通過 defer_bh 函數移動到 done隊列,觸發軟中斷tasklet,來處理tx_done;

2)資料接收完成後 與發送數一樣 通過 defer_bh 來處理rx_done,其中調用了netif_rx來送出資料到網絡層。

 net->change_mtu = usbnet_change_mtu;

 net->get_stats = usbnet_get_stats;

 net->hard_start_xmit = usbnet_start_xmit;

 net->open = usbnet_open;

 net->stop = usbnet_stop; SET_NETDEV_DEV(net, &udev->dev);/*net關聯usb接口*/

 status = register_netdev (net);/*注冊網絡裝置*/

需要注意的函數為 usbnet_start_xmit 與 usbnet_open 。

擷取usb端點位址,有目前的配置擷取bulk in out 的位址,用來收發資料,這部分過程可以參考usb-skeleton.c程式,不做詳細介紹。

3、資料發送

其中 usbnet_start_xmit 為網卡發送函數,有上層調用send函數時,傳下skb資料包。

在 usbnet_start_xmit 中,usb_alloc_urb (0, GFP_ATOMIC)配置設定記憶體---->usb_fill_bulk_urb (urb, dev->udev, dev->out,  skb->data, skb->len, tx_complete, skb) 填充urb----->usb_submit_urb (urb, GFP_ATOMIC)送出urb。

成功時__skb_queue_tail (&dev->txq, skb),在完成函數 tx_complete 函數中 會設定tx_done标志等,然後調用defer_bh來處理。

4、資料的接收

第一種可能:

這是這部分驅動中最難的一部分。接收資料關鍵起始點為 usbnet_open 函數,上層 通過 ifconfig usb0 up 時,open函數被調用了,open函數中 觸發了軟中斷 tasklet_schedule();

static void usbnet_bh (unsigned long param)

{

 struct usbnet  *dev = (struct usbnet *) param;

 struct sk_buff  *skb;

 struct skb_data  *entry;

 while ((skb = skb_dequeue (&dev->done))) {

  entry = (struct skb_data *) skb->cb;

  switch (entry->state) {

  case rx_done:

   entry->state = rx_cleanup;

   rx_process (dev, skb);

   continue;

  case tx_done:

  case rx_cleanup:

   usb_free_urb (entry->urb);

   dev_kfree_skb (skb);

  default:

   devdbg (dev, "bogus skb state %d", entry->state);

  }

 }

···

 if (netif_running (dev->net)

   && netif_device_present (dev->net)

   && !timer_pending (&dev->delay)

   && !test_bit (EVENT_RX_HALT, &dev->flags)) {

  int temp = dev->rxq.qlen;

  int qlen = RX_QLEN (dev);

  if (temp < qlen) {

   struct urb *urb;

   int  i;

   for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {

    urb = usb_alloc_urb (0, GFP_ATOMIC);

    if (urb != NULL)

     rx_submit (dev, urb, GFP_ATOMIC);

   }

   if (temp != dev->rxq.qlen && netif_msg_link (dev))

    devdbg (dev, "rxqlen %d --> %d",

      temp, dev->rxq.qlen);

   if (dev->rxq.qlen < qlen)

    tasklet_schedule (&dev->bh);

}

個人認為藍色部分為接收提供了4個urb來接收資料(full speed),從rx_complete 中可以看出,urb 沒有資料時阻塞 ,該函數未被調用,然後資料進來時,urb又有資料,又調用了rx_submit來送出資料,

rx_complete 函數部分代碼:

 defer_bh(dev, skb, &dev->rxq);  /*在complete函數完成時調用tasklet_bh*/

if (urb) {

  if (netif_running (dev->net)

    && !test_bit (EVENT_RX_HALT, &dev->flags)) {

   rx_submit (dev, urb, GFP_ATOMIC);      /*接收資料*/        

   return;

 ···

第二種可能:

由第一種中 tasklet_bh  藍色部分開始接收數,由于沒有資料進來送出urb時,進入default 又開始調用tasklet,形成循環。

rx_submit部分代碼:

 switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {

  case -EPIPE:

   usbnet_defer_kevent (dev, EVENT_RX_HALT);

   break;

  case -ENOMEM:

   usbnet_defer_kevent (dev, EVENT_RX_MEMORY);

  case -ENODEV:

   if (netif_msg_ifdown (dev))

    devdbg (dev, "device gone");

   netif_device_detach (dev->net);

   if (netif_msg_rx_err (dev))

    devdbg (dev, "rx submit, %d", retval);

   tasklet_schedule (&dev->bh);

  case 0:

   __skb_queue_tail (&dev->rxq, skb);

個人總結:

啃了一個禮拜終于啃完了,隻是對urb的整體機制不夠深入了解呀,但還是把usbnet給裁了,隻支援bulk。歡迎技術交流呀!

奶奶的第一次寫技術部落格。不容易,希望對大家有幫助。USB驅動是個不錯的東西。QQ:497067994

【新浪微網誌】 張昺華--sky

【twitter】 @sky2030_

【facebook】 張昺華 zhangbinghua

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.

繼續閱讀