Linux那些事兒之我是Hub 24 所謂的熱插拔

2021-08-22 11:11:48 字數 3755 閱讀 9011

你問我這世界,最遠的地方在**,我將答案拋向藍天之外落在你心底,你問我這世界,最後的真愛在**,我把線索指向大海之外直達我懷裡.你問我hub_irq()這個函式,最終是被誰呼叫,我卻只能說我既沒有答案也沒有線索.當然,你要是問我芙蓉姐姐還能紅多久,我倒是可以很爽快的告訴你,你知道永遠有多遠嗎?

我們曾經在hub_configure中講過中斷傳輸,當時呼叫了usb_fill_int_urb()函式,並且把hub_irq作為乙個引數傳遞了進去,最終把urb->complete賦值為hub_irq.然後,主機控制器會定期詢問hub,每當hub埠上有乙個裝置插入或者拔除時,它就會向主機控制器打小報告.(怎麼哪都有人打小報告啊,當初在intel不知道哪位哥們缺德,跟老闆說我做事情只關注結果不管過程,只要問題解決了就好,根本不管問題是怎麼被解決的.害得我在intel差點沒過試用期.)具體來說,從硬體的角度看,就是hub會向host controller返回一些資訊,或者說data,這個data被稱作」hub and port status change bitmap」,而從軟體角度來看,host controller的驅動程式接下來會在處理好這個過程的urb之後,呼叫該urb的complete函式,對於hub來說,這個函式就是hub_irq().

337 /* completion function, fires on port status changes and various faults */

338 static void hub_irq(struct urb *urb)

339

368369 hub->nerrors = 0;

370372 kick_khubd(hub);

373374 resubmit:

375 if (hub->quiescing)

376 return;

377378 if ((status = usb_submit_urb (hub->urb, gfp_atomic)) != 0

379 && status != -enodev && status != -eperm)

380 dev_err (hub->intfdev, "resubmit --> %d/n", status);

381 }

你問這個引數urb是哪個urb?告訴你,中斷傳輸就是只有乙個urb,不是說像bulk傳輸那樣每次開啟一次傳輸都要有申請乙個urb,提交urb,對於中斷傳輸,乙個urb就可以了,反覆利用,所以我們只有一次呼叫usb_fill_int_urb()函式.這正體現了中斷互動的週期性.

340行,當初我們填充urb的時候,urb->context就是賦的hub,所以現在這句話就可以獲得我們的那個hub.

345行開始判斷urb的狀態,前三種都是出錯了,直接返回.

351的default和case 0.這段**是我認為最有技術含量的一段**.我不知道我是該恨譚浩強該是該恨我自己,我記得我在譚浩強的書上看到的default總是在各個case之後,結果我以為這裡default不管case等於0與否都會執行,結果半天沒看懂,後來我明白了,其實當urb->status為0的時候,default那一段是不會執行的.

所以這段**就很好理解了.一開始hub->error為0,hub->nerrors也為0,所以default這一段很明顯,goto resubmit,即,我們允許年輕人犯錯誤,並且可以允許犯十次,錯了沒有關係,只要不在同一條陰溝裡翻船翻十次就可以了,我相信這對大多數人來說都足夠了,唯一例外的大概是中國男足了.每次陰溝裡翻船,爬起來一看,咦,怎麼還是上次那條陰溝啊?resubmit那一段就是重新呼叫了一次usb_submit_urb()而已.當然,還判斷了hub->quisecing.這個變數初始值為1,但是我們前面在hub_activate裡把它設定為了0,有乙個函式會把它設定為1,這個函式就是hub_quiesce(),而呼叫後者的只有兩個函式,乙個是hub_suspend,乙個是hub_pre_reset().於是,這裡的意思就很明確了,如果hub被掛起了,或者要被reset了,那麼就不用重新提交urb了,hub_irq()函式直接返回吧.

再看case 0,urb->status為0,說明這個urb被順利的處理了,即host controller獲得了他想要的資料,即那個」hub and port status change bitmap」,因為我們當初呼叫usb_fill_int_urb的時候,把*hub->buffer傳遞給了urb->transfer_buffer,所以這個資料現在就在hub->buffer中,我們來看這個bitmap是什麼樣子,usb spec中給出了這樣一幅圖:

首先這幅圖為我們解答了乙個很大的疑惑.當時在hub_events()中您大概還有一些地方不是很清楚.現在我們可以來弄清楚它們了.

我們回過頭來看,struct usb_hub中,unsigned long event_bits[1],首先這是乙個陣列,其次這個陣列只有乙個元素,而這乙個元素恰恰就是對應這裡的這個bitmap,即所謂的點陣圖,每一位都有其作用.乙個unsigned long至少4個位元組,即32個bits.所以夠用了,而我們看到這張圖里,bit0和其它的bit是不一樣的,bit 0表示hub有變化,而其它bit則具體表示某乙個埠有沒有變化,即bit 1表示埠1有變化,bit 2表示埠2有變化,如果乙個埠沒有變化,對應的那一位就是0.

所以我們可以回到hub_events()函式中來,看看當時我們是如何判斷hub->event_bits的,當時我們有這麼一小段,

2685 /* deal with port status changes */

2686 for (i = 1; i <= hub->descriptor->bnbrports; i++)

2791 if (hubchange & hub_change_overcurrent)

2797 }

而這樣我們就很清楚hub_events()的整體思路了,判斷每個埠是否有變化,如果有變化就去處理它,沒有變化也沒有關係,接下來判斷是否hub整體上有變化,如果有有變化,那麼也去處理它.滿足了個人利益,滿足了集體利益,這不正體現了我們社會主義制度的優越性麼?

關於這個actual_length,其實你不傻的話這個根本就不用我囉嗦了.因為每乙個hub的port是不一樣的,所以這張bitmap的長度就不一樣,比如說你是16個port,那麼這個bitmap最多就只要16+1個bit就足夠了.而actual_length就是3,即3個bytes.因為3個bytes等於24個bits,足以容納16+1個bits了.而struct usb_hub中,buffer是這樣乙個成員,char (*buffer)[8],所以3個bytes就意味著這個buffer的前三個元素裡承載著我們的希望.這樣我們就不難理解這裡這個hub->event_bits是如何變成為這張bitmap的了.

369行,把nerrors清零吧,過去的事情就讓它過去吧,讓我們忘記過去,展望未來.其實這道理不用我說,小剛那首就說的很清楚了:有太多往事就別喝下太少酒精,太珍惜生命就別隨便掏心,捨不得看破就別張開眼睛,想開心就要捨得傷心;有太多行李就別單獨旅行,不能夠離開就不要接近,捨不得結束就別開始一段感情,想忘記就要一切歸零.謝謝林夕,用這麼好的歌詞為我們詮釋了linux核心**.

最關鍵的當然還是372行,再次呼叫kick_khubd()函式,於是會再一次觸發hub_events().

而hub_irq()函式也就到這裡了.這個函式不長,可是很重要,其中最重要的正是最後這句kick_khubd().而這也就是所謂的熱插拔的實現路徑,要知道我們上次分析kick_khubd的時候是在hub初始化的時候,即那次針對的情況是裝置一開始就插在了hub上,而這裡再次呼叫kick_khubd才是真正的在使用過程中,突然間hub有了變化的情況,應該說這個後者才是真正有技術含量的冬冬.

讀Linux那些事兒之我是HUB筆記(一)

革命尚未成功,繼續看我們的hub 1 usb裝置能夠使用條件 乙個是 usbcore,這就是核心模組,另乙個是主機控制器的驅動程式 usb host controller.乙個是 echi的,三個是uhci,就是host controller 的介面 hub 叫做集線器 裝置與host contro...

讀Linux那些事兒之我是HUB筆記(二)

12 events 通過ps el檢視 events被叫做工作者執行緒,或者說 worker threads,更確切的說,這些應該是預設的工作者執行緒.而與工作者執行緒相關的乙個概念就是工作佇列 或者叫 work queue.工作佇列的作用 是把工作推後,交由乙個核心執行緒去執行,更直接的說就是如果...

Linux那些事兒之我是EHCI 引子

轉眼之間,到了2008年,先祝大家新年快樂,希望新的一年裡好運連連,工資猛漲。好久沒有寫了,乙個原因在於,作 為乙個phd學生,難免要做一些讀 寫 的瑣事,另乙個原因就是自己太懶了。大哥甲一如既往,堅持的寫作,著實讓人欽佩。此時此刻,我情 不自禁,作詩一首,北飄奇男子,江南大丈夫。海上常常生明月,江...