Linux那些事儿 之 戏说USB(20)设备的生命线(三)
生活随笔
收集整理的這篇文章主要介紹了
Linux那些事儿 之 戏说USB(20)设备的生命线(三)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
函數usb_control_msg調用了usb_internal_control_msg之后就去一邊兒睡大覺了,臟活兒累活兒,全部留給usb_internal_control_msg去做了。
一個中心:struct urb結構體,就是咱們前面多次提到又多次飄過,只聞其名不見其形的傳說中的urb,全稱usb request block,站在咱們的角度看,usb通信靠的就是它這張臉。
第一個基本點:usb_alloc_urb函數,創建一個urb,struct urb結構體只能使用它來創建,它是urb在usb世界里的獨家代理。
第二個基本點:usb_fill_control_urb函數,初始化一個控制urb,urb被創建之后,使用之前必須要正確的初始化。
第三個基本點:usb_start_wait_urb函數,將urb提交給咱們的usb core,以便分配給特定的主機控制器驅動進行處理,然后默默的等待處理結果,或者超時。
很早很早很早很早以前就說過,主機與設備之間通過管道來傳輸數據,管道的一端是主機上的一個緩沖區,另一端是設備上的端點。管道之中流動的數據,在主機控制器和設備看來是一個個packets,在咱們看來就是urb。因而,端點之中就有那么一個隊列,叫urb隊列。不過,這并不代表一個urb只能發配給一個端點,它可能通過不同的管道發配給不同的端點,那么這樣一來,我們如何知道這個urb正在被多少個端點使用,如何判斷這個urb的生命已經over?如果沒有任何一個端點在使用它,而我們又無法判斷這種情況,它就會永遠的飄蕩在usb的世界里。我們需要尋求某種辦法在這種情況下給它們一個好的歸宿,這就是引用計數。每多一個使用者,它的這個引用計數就加1,每減少一個使用者,引用計數就減一,如果連最后一個使用者都釋放了這個urb,宣稱不再使用它了,那它的生命周期就走到了盡頭,會自動的銷毀。
接下來就是第二個問題,如何來表示這個神奇的引用計數?其實它是一個struct kref結構體,在include/linux/kref.h里定義
整個kref.c文件就定義了這么三個函數,kref_init初始化,kref_get將引用計數加1,kref_put調用kref_sub將引用計數減一并判斷是不是為0,為0的話就調用參數里release函數指針指向的函數把對象銷毀掉。它們對獨苗兒refcount的操作都是通過原子變量特有的操作函數,其實這句話可以當選當日最大廢話,原子變量當然要使用專門的操作函數了,編譯器還能做些優化,否則直接使用一般的變量就可以了干嗎還要使用原子變量,不是沒事找事兒么,再說如果你直接像對待一般整型值一樣對待它,編譯器也會看不過去你的行為,直接給你個error的。友情提醒一下,kref_init初始化時,是把refcount的值初始化為1了的,不是0。還有一點要說的是kref_put參數里的那個函數指針,你不能傳遞一個NULL過去,否則這個引用計數就只是計數,而背離了最初的目的,要記住我們需要在這個計數減為0的時候將嵌入這個引用計數struct kref結構體的對象給銷毀掉,所以這個函數指針也不能為kfree,因為這樣的話就只是把這個struct kref結構體給銷毀了,而不是整個對象。
第三個問題,如何使用struct kref結構來為我們的對象計數?當然我們需要把這樣一個結構嵌入到你希望計數的對象里邊,不然你根本就無法對對象在它整個生命周期里的使用情況作出判斷,難道還真能以為Linus是linux里的太陽,像太陽神阿波羅一樣掐指一算就知道誰誰在哪兒待過,現在在哪兒。但是我們應該是幾乎見不到內核里邊兒直接使用上面那幾個函數來給對象計數的,而是每種對象又定義了自己專用的引用計數函數,比如咱們的urb,在drivers/usb/core/urb.c里定義
到此,世界如此美麗,引用計數如此簡單,不是么?
lock,一把自旋鎖。韋唯早就唱了,每個urb都有一把自旋鎖。
hcpriv,走到今天,你應該明白這個urb最終還是要提交給主機控制器驅動的,這個字段就是urb里主機控制器驅動的自留地,咱們就不插手了。
use_count,這里又是一個使用計數,不過此計數非彼計數,它與上面那個用來追蹤urb生命周期的kref一點兒血緣關系也沒有,連遠親都不是。那它是用來做什么的,憑什么在臃腫的struct urb不斷喊著要瘦身的時候還仍有一席之地?
先了解下使用urb來完成一次完整的usb通信都要經歷哪些階段,首先,驅動程序發現自己有與usb設備通信的需要,于是創建一個urb,并指定它的目的地是設備上的哪個端點,然后提交給usb core,usb core將它修修補補的做些美化之后再移交給主機控制器的驅動程序HCD,HCD會去解析這個urb,了解它的目的是什么,并與usb設備進行相應的交流,在交流結束,urb的目的達到之后,HCD再把這個urb的所有權移交回驅動程序。
這里的use_count就是在usb core將urb移交給HCD,辦理移交手續的時候,插上了那么一腳,每當走到這一步,它的值就會加1。什么時候減1?在HCD重新將urb的所有權移交回驅動程序的時候。這樣說吧,只要HCD擁有這個urb的所有權,那么該urb的use_count就不會為0。這么一說,似乎use_count也有點追蹤urb生命周期的味道了,當它的值大于0時,就表示當前有HCD正在處理它,和上面的kref概念上有部分的重疊,不過,顯然它們之間是有區別的,沒區別的話這兒干嗎要用兩個計數,不是沒事兒找抽么。上面的那個kref實現方式是內核里統一的引用計數機制,當計數減為0時,urb對象就被urb_destroy給銷毀了。這里的use_count只是用來統計當前這個urb是不是正在被哪個HCD處理,即使它的值為0,也只是說明沒有HCD在使用它而已,并不代表就得把它給銷毀掉。比方說,HCD利用完了urb,把它還給了驅動,這時驅動還可以對這個urb檢修檢修,再提交給哪個HCD去使用。
下面的問題就是既然它不會平白無故的多出來,那它究竟是用來干啥的?還要從剛提到的那幾個階段說起。urb驅動也創建了,提交也提交了,HCD正處理著那,可驅動反悔了,它不想再繼續這次通信了,想將這個urb給終止掉,善解任意的usb core當然會給驅動提供這樣的接口來滿足這樣的需要。不過這個需要還被寫代碼的哥們兒細分為兩種,一種是驅動只想通過usb core告訴HCD一聲,說這個urb我想終止掉,您就別費心再處理了,然后它不想在那里等著HCD的處理,想忙別的事去,這就是俗稱的異步,對應的是usb_unlink_urb函數。當然對應的還有種同步的,驅動會在那里苦苦等候著HCD的處理結果,等待著urb被終止,對應的是usb_kill_urb函數。而HCD將這次通信終止后,同樣會將urb的所有權移交回驅動。那么驅動通過什么判斷HCD已經終止了這次通信?就是通過這里的use_count,驅動會在usb_kill_urb里面一直等待著這個值變為0。
reject,拒絕,拒絕什么?在目前版本的內核里,只有usb_kill_urb函數有特權對它進行修改,那么,顯然reject就與上面說的urb終止有關了。那就看看drivers/usb/core/urb.c里定義的這個函數
寫代碼的哥們兒也都是憂國憂民的主兒,也深刻體會到廣大男同胞們的無奈,于是提供了might_sleep函數,用它來判斷一下這個函數是不是處在能夠休眠的情況,如果不是,就會打印出一大堆的堆棧信息,比如你在中斷上下文調用了這個函數時。不過,它也就是基于調試的目的用一用,方便日后找錯,并不能強制哪個函數改變自己的上下文。
4行,這里就是判斷一下urb,urb要去的那個設備,還有那個設備在的總線有沒有,如果哪個不存在,就還是返回吧。
6行,將reject加1。加1有什么用?其實目前版本的內核里只有兩個地方用到了這個值進行判斷。第一個地方是在usb core將urb提交給HCD,正在辦移交手續的時候,如果reject大于0,就不再接著移交了,也就是說這個urb被HCD給拒絕了。這是為了防止這邊兒正在終止這個urb,那邊兒某個地方卻又妄想將這個urb重新提交給HCD。
8行,這里告訴HCD驅動要終止這個urb了,usb_hcd_unlink_urb函數也只是告訴HCD一聲,然后不管HCD怎么處理就返回了。
9行,上面的usb_hcd_unlink_urb是返回了,但并不代表HCD已經將urb給終止了,HCD可能沒那么快,所以這里usb_kill_urb要休息休息,等人通知它。這里使用了wait_event宏來實現休眠,usb_kill_urb_queue是在/drivers/usb/core/hcd.h里定義的一個等待隊列,專門給usb_kill_urb休息用的。需要注意的是這里的喚醒條件,use_count必須等于0,終于看到use_count實戰的地方了。
那在哪里喚醒正在睡大覺的usb_kill_urb?這牽扯到了第二個使用reject來做判斷的地方。在HCD將urb的所有權返還給驅動的時候,會對reject進行判斷,如果reject大于0,就調用wake_up喚醒在usb_kill_urb_queue上休息的usb_kill_urb。也好理解,HCD都要將urb的所有權返回給驅動了,那當然就是已經處理完了,放在這里就是已經將這個urb終止了,usb_kill_urb等的就是這一天的到來,當然就要醒過來繼續往下走了。
11行,將reject剛才增加的那個1給減掉。urb都已經終止了,也沒人再會去拒絕它了,reject還是開始什么樣兒結束的時候就什么樣吧。
索性將usb_unlink_urb函數也貼出來看看它們之間有什么區別吧
struct urb結構里的前邊兒這幾個,只是usb core和主機控制器驅動需要關心的,實際的驅動里根本用不著也管不著,它們就是usb和HCD的后花園,想種點什么不種點什么都由寫這塊兒代碼的哥們兒決定,他們在里面怎么為所欲為都不關你寫驅動的啥事。usb在linux里起起伏伏這么多年,前邊兒的這些內容早就變過多少次,說不定你今天還看到誰誰,到接下來的哪天就看不到了,不過,變化的是形式,不變的是道理。驅動要做的只是創建一個urb,然后初始化,再把它提交給usb core就可以了,使用不使用引用計數,加不加鎖之類的一點都不用去操心。感謝David Brownell,感謝Alan Stern,感謝……,沒有他們就沒有usb在linux里的今天。
drivers/usb/core/message.c
static int usb_internal_control_msg(struct usb_device *usb_dev,unsigned int pipe,struct usb_ctrlrequest *cmd,void *data, int len, int timeout)
{struct urb *urb;int retv;int length;urb = usb_alloc_urb(0, GFP_NOIO);if (!urb)return -ENOMEM;usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,len, usb_api_blocking_completion, NULL);retv = usb_start_wait_urb(urb, timeout, &length);if (retv < 0)return retv;elsereturn length;
}這個函數粗看過去,可以概括為一個中心,三個基本點,以一個struct urb結構體為中心,以usb_alloc_urb、usb_fill_control_urb、usb_start_wait_urb三個函數為基本點。
一個中心:struct urb結構體,就是咱們前面多次提到又多次飄過,只聞其名不見其形的傳說中的urb,全稱usb request block,站在咱們的角度看,usb通信靠的就是它這張臉。
第一個基本點:usb_alloc_urb函數,創建一個urb,struct urb結構體只能使用它來創建,它是urb在usb世界里的獨家代理。
第二個基本點:usb_fill_control_urb函數,初始化一個控制urb,urb被創建之后,使用之前必須要正確的初始化。
第三個基本點:usb_start_wait_urb函數,將urb提交給咱們的usb core,以便分配給特定的主機控制器驅動進行處理,然后默默的等待處理結果,或者超時。
struct urb {/* private: usb core and host controller only fields in the urb */struct kref kref; /* reference count of the URB */void *hcpriv; /* private data for host controller */atomic_t use_count; /* concurrent submissions counter */atomic_t reject; /* submissions will fail */int unlinked; /* unlink error code *//* public: documented fields in the urb that can be used by drivers */struct list_head urb_list; /* list head for use by the urb's* current owner */struct list_head anchor_list; /* the URB may be anchored */struct usb_anchor *anchor;struct usb_device *dev; /* (in) pointer to associated device */struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */unsigned int pipe; /* (in) pipe information */unsigned int stream_id; /* (in) stream ID */int status; /* (return) non-ISO status */unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/void *transfer_buffer; /* (in) associated data buffer */dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */struct scatterlist *sg; /* (in) scatter gather buffer list */int num_mapped_sgs; /* (internal) mapped sg entries */int num_sgs; /* (in) number of entries in the sg list */u32 transfer_buffer_length; /* (in) data buffer length */u32 actual_length; /* (return) actual transfer length */unsigned char *setup_packet; /* (in) setup packet (control only) */dma_addr_t setup_dma; /* (in) dma addr for setup_packet */int start_frame; /* (modify) start frame (ISO) */int number_of_packets; /* (in) number of ISO packets */int interval; /* (modify) transfer interval* (INT/ISO) */int error_count; /* (return) number of ISO errors */void *context; /* (in) context for completion */usb_complete_t complete; /* (in) completion routine */struct usb_iso_packet_descriptor iso_frame_desc[0];/* (in) ISO ONLY */
};kref,urb的引用計數。甭看它是隱藏在urb內部的一個不起眼的小角色,但小角色做大事情,它決定了一個urb的生死存亡。一個urb有用沒用,是繼續委以重任還是無情銷毀都要看它的臉色。那第一個問題就來了,為什么urb的生死要掌握在這個小小的引用計數手里邊兒?
很早很早很早很早以前就說過,主機與設備之間通過管道來傳輸數據,管道的一端是主機上的一個緩沖區,另一端是設備上的端點。管道之中流動的數據,在主機控制器和設備看來是一個個packets,在咱們看來就是urb。因而,端點之中就有那么一個隊列,叫urb隊列。不過,這并不代表一個urb只能發配給一個端點,它可能通過不同的管道發配給不同的端點,那么這樣一來,我們如何知道這個urb正在被多少個端點使用,如何判斷這個urb的生命已經over?如果沒有任何一個端點在使用它,而我們又無法判斷這種情況,它就會永遠的飄蕩在usb的世界里。我們需要尋求某種辦法在這種情況下給它們一個好的歸宿,這就是引用計數。每多一個使用者,它的這個引用計數就加1,每減少一個使用者,引用計數就減一,如果連最后一個使用者都釋放了這個urb,宣稱不再使用它了,那它的生命周期就走到了盡頭,會自動的銷毀。
接下來就是第二個問題,如何來表示這個神奇的引用計數?其實它是一個struct kref結構體,在include/linux/kref.h里定義
struct kref {atomic_t refcount;
};
這個結構與struct urb相比簡約到極致了,簡直就是迎著咱們的口味來的。不過別看它簡單,內核里就是使用它來判斷一個對象還有沒有用的。它里邊兒只包括了一個原子變量,為什么是原子變量?既然都使用引用計數了,那就說明可能同時有多個地方在使用這個對象,總要考慮一下它們同時修改這個計數的可能性吧,也就是俗稱的并發訪問,那怎么辦?加個鎖?就這么一個整數值專門加個鎖未免也忒大材小用了些,所以就使用了原子變量。圍繞這個結構,內核里還定義了幾個專門操作引用計數的函數,它們在lib/kref.c里定義
/*** kref_init - initialize object.* @kref: object in question.*/
static inline void kref_init(struct kref *kref)
{atomic_set(&kref->refcount, 1);
}/*** kref_get - increment refcount for object.* @kref: object.*/
static inline void kref_get(struct kref *kref)
{/* If refcount was 0 before incrementing then we have a race* condition when this kref is freeing by some other thread right now.* In this case one should use kref_get_unless_zero()*/WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
}/*** kref_sub - subtract a number of refcounts for object.* @kref: object.* @count: Number of recounts to subtract.* @release: pointer to the function that will clean up the object when the* last reference to the object is released.* This pointer is required, and it is not acceptable to pass kfree* in as this function. If the caller does pass kfree to this* function, you will be publicly mocked mercilessly by the kref* maintainer, and anyone else who happens to notice it. You have* been warned.** Subtract @count from the refcount, and if 0, call release().* Return 1 if the object was removed, otherwise return 0. Beware, if this* function returns 0, you still can not count on the kref from remaining in* memory. Only use the return value if you want to see if the kref is now* gone, not present.*/
static inline int kref_sub(struct kref *kref, unsigned int count,void (*release)(struct kref *kref))
{WARN_ON(release == NULL);if (atomic_sub_and_test((int) count, &kref->refcount)) {release(kref);return 1;}return 0;
}/*** kref_put - decrement refcount for object.* @kref: object.* @release: pointer to the function that will clean up the object when the* last reference to the object is released.* This pointer is required, and it is not acceptable to pass kfree* in as this function. If the caller does pass kfree to this* function, you will be publicly mocked mercilessly by the kref* maintainer, and anyone else who happens to notice it. You have* been warned.** Decrement the refcount, and if 0, call release().* Return 1 if the object was removed, otherwise return 0. Beware, if this* function returns 0, you still can not count on the kref from remaining in* memory. Only use the return value if you want to see if the kref is now* gone, not present.*/
static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{return kref_sub(kref, 1, release);
}
整個kref.c文件就定義了這么三個函數,kref_init初始化,kref_get將引用計數加1,kref_put調用kref_sub將引用計數減一并判斷是不是為0,為0的話就調用參數里release函數指針指向的函數把對象銷毀掉。它們對獨苗兒refcount的操作都是通過原子變量特有的操作函數,其實這句話可以當選當日最大廢話,原子變量當然要使用專門的操作函數了,編譯器還能做些優化,否則直接使用一般的變量就可以了干嗎還要使用原子變量,不是沒事找事兒么,再說如果你直接像對待一般整型值一樣對待它,編譯器也會看不過去你的行為,直接給你個error的。友情提醒一下,kref_init初始化時,是把refcount的值初始化為1了的,不是0。還有一點要說的是kref_put參數里的那個函數指針,你不能傳遞一個NULL過去,否則這個引用計數就只是計數,而背離了最初的目的,要記住我們需要在這個計數減為0的時候將嵌入這個引用計數struct kref結構體的對象給銷毀掉,所以這個函數指針也不能為kfree,因為這樣的話就只是把這個struct kref結構體給銷毀了,而不是整個對象。
第三個問題,如何使用struct kref結構來為我們的對象計數?當然我們需要把這樣一個結構嵌入到你希望計數的對象里邊,不然你根本就無法對對象在它整個生命周期里的使用情況作出判斷,難道還真能以為Linus是linux里的太陽,像太陽神阿波羅一樣掐指一算就知道誰誰在哪兒待過,現在在哪兒。但是我們應該是幾乎見不到內核里邊兒直接使用上面那幾個函數來給對象計數的,而是每種對象又定義了自己專用的引用計數函數,比如咱們的urb,在drivers/usb/core/urb.c里定義
void usb_init_urb(struct urb *urb)
{if (urb) {memset(urb, 0, sizeof(*urb));kref_init(&urb->kref);INIT_LIST_HEAD(&urb->anchor_list);}
}
void usb_free_urb(struct urb *urb)
{if (urb)kref_put(&urb->kref, urb_destroy);
}
struct urb *usb_get_urb(struct urb *urb)
{if (urb)kref_get(&urb->kref);return urb;
}usb_init_urb、usb_get_urb、usb_free_urb這三個函數分別調用了前面看到的struct kref結構的三個操作函數來進行引用計數的初始化、加1、減一。什么叫封裝?這就叫封裝。usb_init_urb和usb_get_urb都沒什么好說的,比較感興趣的是usb_free_urb里給kref_put傳遞的那個函數urb_destroy,它也在urb.c里定義
static void urb_destroy(struct kref *kref)
{struct urb *urb = to_urb(kref);if (urb->transfer_flags & URB_FREE_BUFFER)kfree(urb->transfer_buffer);kfree(urb);
}這個urb_destroy首先調用了to_urb,實際上就是一個container_of來獲得引用計數關聯的那個urb,然后使用kfree將它銷毀。
到此,世界如此美麗,引用計數如此簡單,不是么?
lock,一把自旋鎖。韋唯早就唱了,每個urb都有一把自旋鎖。
hcpriv,走到今天,你應該明白這個urb最終還是要提交給主機控制器驅動的,這個字段就是urb里主機控制器驅動的自留地,咱們就不插手了。
use_count,這里又是一個使用計數,不過此計數非彼計數,它與上面那個用來追蹤urb生命周期的kref一點兒血緣關系也沒有,連遠親都不是。那它是用來做什么的,憑什么在臃腫的struct urb不斷喊著要瘦身的時候還仍有一席之地?
先了解下使用urb來完成一次完整的usb通信都要經歷哪些階段,首先,驅動程序發現自己有與usb設備通信的需要,于是創建一個urb,并指定它的目的地是設備上的哪個端點,然后提交給usb core,usb core將它修修補補的做些美化之后再移交給主機控制器的驅動程序HCD,HCD會去解析這個urb,了解它的目的是什么,并與usb設備進行相應的交流,在交流結束,urb的目的達到之后,HCD再把這個urb的所有權移交回驅動程序。
這里的use_count就是在usb core將urb移交給HCD,辦理移交手續的時候,插上了那么一腳,每當走到這一步,它的值就會加1。什么時候減1?在HCD重新將urb的所有權移交回驅動程序的時候。這樣說吧,只要HCD擁有這個urb的所有權,那么該urb的use_count就不會為0。這么一說,似乎use_count也有點追蹤urb生命周期的味道了,當它的值大于0時,就表示當前有HCD正在處理它,和上面的kref概念上有部分的重疊,不過,顯然它們之間是有區別的,沒區別的話這兒干嗎要用兩個計數,不是沒事兒找抽么。上面的那個kref實現方式是內核里統一的引用計數機制,當計數減為0時,urb對象就被urb_destroy給銷毀了。這里的use_count只是用來統計當前這個urb是不是正在被哪個HCD處理,即使它的值為0,也只是說明沒有HCD在使用它而已,并不代表就得把它給銷毀掉。比方說,HCD利用完了urb,把它還給了驅動,這時驅動還可以對這個urb檢修檢修,再提交給哪個HCD去使用。
下面的問題就是既然它不會平白無故的多出來,那它究竟是用來干啥的?還要從剛提到的那幾個階段說起。urb驅動也創建了,提交也提交了,HCD正處理著那,可驅動反悔了,它不想再繼續這次通信了,想將這個urb給終止掉,善解任意的usb core當然會給驅動提供這樣的接口來滿足這樣的需要。不過這個需要還被寫代碼的哥們兒細分為兩種,一種是驅動只想通過usb core告訴HCD一聲,說這個urb我想終止掉,您就別費心再處理了,然后它不想在那里等著HCD的處理,想忙別的事去,這就是俗稱的異步,對應的是usb_unlink_urb函數。當然對應的還有種同步的,驅動會在那里苦苦等候著HCD的處理結果,等待著urb被終止,對應的是usb_kill_urb函數。而HCD將這次通信終止后,同樣會將urb的所有權移交回驅動。那么驅動通過什么判斷HCD已經終止了這次通信?就是通過這里的use_count,驅動會在usb_kill_urb里面一直等待著這個值變為0。
reject,拒絕,拒絕什么?在目前版本的內核里,只有usb_kill_urb函數有特權對它進行修改,那么,顯然reject就與上面說的urb終止有關了。那就看看drivers/usb/core/urb.c里定義的這個函數
void usb_kill_urb(struct urb *urb)
{might_sleep();if (!(urb && urb->dev && urb->ep))return;atomic_inc(&urb->reject);usb_hcd_unlink_urb(urb, -ENOENT);wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);atomic_dec(&urb->reject);
}3行,因為usb_kill_urb函數要一直等候著HCD將urb終止掉,它必須是可以休眠的,不然就太可惡了,就像那些腳踩多只船的,占著本來就稀有的ppmm資源,讓大批男同志們找不到另一半來關愛。所以說usb_kill_urb不能用在中斷上下文,必須能夠休眠將自己占的資源給讓出來。
寫代碼的哥們兒也都是憂國憂民的主兒,也深刻體會到廣大男同胞們的無奈,于是提供了might_sleep函數,用它來判斷一下這個函數是不是處在能夠休眠的情況,如果不是,就會打印出一大堆的堆棧信息,比如你在中斷上下文調用了這個函數時。不過,它也就是基于調試的目的用一用,方便日后找錯,并不能強制哪個函數改變自己的上下文。
4行,這里就是判斷一下urb,urb要去的那個設備,還有那個設備在的總線有沒有,如果哪個不存在,就還是返回吧。
6行,將reject加1。加1有什么用?其實目前版本的內核里只有兩個地方用到了這個值進行判斷。第一個地方是在usb core將urb提交給HCD,正在辦移交手續的時候,如果reject大于0,就不再接著移交了,也就是說這個urb被HCD給拒絕了。這是為了防止這邊兒正在終止這個urb,那邊兒某個地方卻又妄想將這個urb重新提交給HCD。
8行,這里告訴HCD驅動要終止這個urb了,usb_hcd_unlink_urb函數也只是告訴HCD一聲,然后不管HCD怎么處理就返回了。
9行,上面的usb_hcd_unlink_urb是返回了,但并不代表HCD已經將urb給終止了,HCD可能沒那么快,所以這里usb_kill_urb要休息休息,等人通知它。這里使用了wait_event宏來實現休眠,usb_kill_urb_queue是在/drivers/usb/core/hcd.h里定義的一個等待隊列,專門給usb_kill_urb休息用的。需要注意的是這里的喚醒條件,use_count必須等于0,終于看到use_count實戰的地方了。
那在哪里喚醒正在睡大覺的usb_kill_urb?這牽扯到了第二個使用reject來做判斷的地方。在HCD將urb的所有權返還給驅動的時候,會對reject進行判斷,如果reject大于0,就調用wake_up喚醒在usb_kill_urb_queue上休息的usb_kill_urb。也好理解,HCD都要將urb的所有權返回給驅動了,那當然就是已經處理完了,放在這里就是已經將這個urb終止了,usb_kill_urb等的就是這一天的到來,當然就要醒過來繼續往下走了。
11行,將reject剛才增加的那個1給減掉。urb都已經終止了,也沒人再會去拒絕它了,reject還是開始什么樣兒結束的時候就什么樣吧。
索性將usb_unlink_urb函數也貼出來看看它們之間有什么區別吧
int usb_unlink_urb(struct urb *urb)
{if (!urb)return -EINVAL;if (!urb->dev)return -ENODEV;if (!urb->ep)return -EIDRM;return usb_hcd_unlink_urb(urb, -ECONNRESET);
}看usb_unlink_urb這兒就簡單多了,只是把自己的意愿告訴HCD,然后就非常灑脫的返回了。
struct urb結構里的前邊兒這幾個,只是usb core和主機控制器驅動需要關心的,實際的驅動里根本用不著也管不著,它們就是usb和HCD的后花園,想種點什么不種點什么都由寫這塊兒代碼的哥們兒決定,他們在里面怎么為所欲為都不關你寫驅動的啥事。usb在linux里起起伏伏這么多年,前邊兒的這些內容早就變過多少次,說不定你今天還看到誰誰,到接下來的哪天就看不到了,不過,變化的是形式,不變的是道理。驅動要做的只是創建一個urb,然后初始化,再把它提交給usb core就可以了,使用不使用引用計數,加不加鎖之類的一點都不用去操心。感謝David Brownell,感謝Alan Stern,感謝……,沒有他們就沒有usb在linux里的今天。
總結
以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(20)设备的生命线(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux那些事儿 之 戏说USB(19
- 下一篇: Linux那些事儿 之 戏说USB(22