天天看點

Linux那些事兒之我是UHCI(22)Root Hub的中斷傳輸

來看中斷傳輸,中斷傳輸和下面要講的等時傳輸無疑要比之前的那兩種傳輸複雜些,至少它們講究一個周期性.當年歌壇大姐大那英在看到usb子系統中對這兩種傳輸的實作的複雜性之後,頗為感慨的對寫代碼的哥們兒唱出了那句”就這樣被你征服”,而其多年來的老對手田震看了之後心情抑郁,一氣之下,嗓子永久性的嘶啞了,但仍然呼籲後人看這些代碼的時候要”執著”.是以我們看代碼的時候看不懂也不用灰心,歌手林志炫的成名曲<<單身情歌>>也就是把自己看代碼的親身經曆唱了出來:為了看代碼孤軍奮鬥,早就吃夠了看代碼的苦,在代碼中失落的人到處有,而我隻是其中一個.毫無疑問,這首歌唱出了我們看代碼的心聲,是以歌曲一問世便得到了廣大Linux愛好者的追捧并迅速竄紅.

和前面那個控制傳輸一樣,中斷傳輸的代碼也分為兩部分,一個是針對Root Hub的,這部分相當簡單,另一個是針對非Root Hub的,這一部分明顯複雜許多.我們先來看Root Hub的.從哪裡開始看應該不用多說了吧,這些年裡,雖然很多事情都已經像滄海變成了桑田,但是咱們跟蹤urb的入口依然和<<曲苑雜壇>>的主持人一樣,多少年也不會變,依然是usb_submit_urb.

還記得咱們在hub驅動中講的那個hub_probe嗎?在hub驅動的探測過程中,最終咱們會送出一個urb.是一個中斷urb.那麼咱們來看調用usb_submit_urb()來submit這個urb之後究竟會發生什麼?

但是與之前控制傳輸Bulk傳輸不同的是,之前我們是淩波微步來到了最後一行usb_hcd_submit_urb,而現在在這一行之前還有幾行是我們需要關注的.

首先我們注意到243行對臨時變量temp賦了值,看到它被指派為usb_pipetype(pipe),我相信即使是後海的酒事先串通的人也知道,從此以後temp就是管道類型.

于是我們像草上飛一樣飛到338行,看到這裡有一個switch.所有的痛苦都來自選擇,所謂幸福,就是沒有選擇.看到這裡你明白為何當初在講控制傳輸和Bulk傳輸的時候我們跳過了這一段了吧,沒錯,這裡隻有兩個case,即PIPE_ISOCHRONOUS和PIPE_INTERRUPT,這兩個case就是等時管道和中斷管道.而控制傳輸和Bulk傳輸根本不在這一個選擇的考慮範疇之内.是以當時我們很幸福的飄了過去.但現在不行了.實際上這裡對于等時傳輸和對于中斷傳輸,處理方法是一樣的.

首先判斷urb->interval,我們在hub驅動中已經講過它的作用,它當然不能小于等于0.

其次根據裝置是高速還是全速低速,再一次設定interval.我們當時在hub驅動中也說過,對于高速裝置和全速低速裝置,這個interval的機關是不一樣的.前者的機關是微幀,後者的機關是幀,是以這裡對它們有不同的處理方法,但是總的來說,我們可以看到temp無論如何會是2的整數次方,是以372行這麼一指派的效果是,如果你的期待值是介于2的n次方和2的n+1次方之間,那麼我們就把它設定成2的n次方.因為最終設定成2的整數次方對我們來說軟體上便于實作,而硬體上來說也無所謂,因為usb spec中也是允許的,比如,usb spec 2.0 5.7.4中有這麼一段:

The period provided by the system may be shorter than that desired by the device up to the shortest period defined by the USB (125 μs microframe or 1 ms frame). The client software and device can depend only on the fact that the host will ensure that the time duration between two transaction attempts with the endpoint will be no longer than the desired period.

這段話的意思很明确,比如你背着老婆包了一個情婦,她希望你每隔7天去關心她一次,而你如果每隔4天關心她一次那她當然沒意見,但如果你每隔10天關心她一次她心裡就有想法了.

關于ISO傳輸的那部分代碼咱們後面再看,現在依然先飄過.然後我們就再一次進入了usb_hcd_submit_urb.

而對于Root Hub,我們又再一次進入了rh_urb_enqueue,對于中斷傳輸,我們進入了rh_queue_status.這個函數來自drivers/usb/core/hcd.c:

    599

    600 static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)

    601 {

    602         int             retval;

    603         unsigned long   flags;

    604         int             len = 1 + (urb->dev->maxchild / 8);

    605

    606         spin_lock_irqsave (&hcd_root_hub_lock, flags);

    607         if (urb->status != -EINPROGRESS)       

    608                 retval = urb->status;

    609         else if (hcd->status_urb || urb->transfer_buffer_length < len) {

    610                 dev_dbg (hcd->self.controller, "not queuing rh status urb/n");

    611                 retval = -EINVAL;

    612         } else {

    613                 hcd->status_urb = urb;

    614                 urb->hcpriv = hcd;     

    615

    616                 if (!hcd->uses_new_polling)

    617                         mod_timer (&hcd->rh_timer, jiffies +

    618                                         msecs_to_jiffies(250));

    619

    620                

    621                 else if (hcd->poll_pending)

    622                         mod_timer (&hcd->rh_timer, jiffies);

    623                 retval = 0;

    624         }

    625         spin_unlock_irqrestore (&hcd_root_hub_lock, flags);

    626         return retval;

    627 }

還好這個函數不那麼變态,由于我們設定了hcd->uses_new_polling為1,而hcd->poll_pending隻有在一個地方被改變,即usb_hcd_poll_rh_status(),如果這個函數被調用了而Hub端口處沒什麼變化,那麼poll_pending就會設定為1.但當我們第一次來到這個函數的時候,poll_pending還沒有被設定過,是以它隻能是0.

假設咱們第一次執行usb_hcd_poll_rh_status的時候,Root Hub的端口确實沒有什麼資訊,即沒有連接配接任何usb裝置并且沒有任何需要彙報的資訊,那麼poll_pending就會設定為1.是以下一次當然來到這個函數的時候,622行這個mod_timer會被執行.是以我們将再一次執行usb_hcd_poll_rh_status,并且是立即執行.但關于usb_hcd_poll_rh_status,咱們也沒什麼好講的,當初我們已經詳細的講過了.是以基本上我們就知道了,如果Root Hub的端口沒有什麼改變的話,usb_submit_urb為Root Hub而送出的中斷urb也不幹什麼正經事,我們能看到的是rh_queue_status,rh_urb_enqueue,usb_hcd_submit_urb,usb_submit_urb這四個函數像多米諾骨牌一樣一個一個傳回0.然後生活還會繼續,然後Tomorrow is another day,即使Hub端口裡永遠不接入任何裝置,驅動程式也仍然像沈祥福帶超白金一代時沖擊雅典奧運會時說過的那句”我們還活着”.

不過我最後想提醒一點,由于hub driver中的usb_submit_urb是在hub_probe的過程中被執行的,而這時候實際上咱們正處在register_root_hub中,也就是說,咱們是在usb_add_hcd中,回過去看這個函數你會發現,1639行調用register_root_hub,而1643行調用usb_hcd_poll_rh_status.這也就是說,盡管咱們很早之前就講過了usb_hcd_poll_rh_status這個函數,但是實際上第一次調用usb_hcd_poll_rh_status發生在rh_queue_status之後.這也就是為什麼這裡我說第一次進入rh_queue_status的時候,poll_pending的值為0,因為隻有調用了usb_hcd_poll_rh_status之後,poll_pending才有可能變成1.

繼續閱讀