1、驅動流程:

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