這個(gè)故事中使用的是2.6.10的內(nèi)核代碼.Linux內(nèi)核代碼目錄中, 所有去設(shè)備驅(qū)動(dòng)程序有關(guān)的代碼都在drivers/目錄下面,在這個(gè)目錄中我們用ls命令可以看到很多子目錄. localhost:/usr/src/linux-2.6.10/drivers # ls 其中usb目錄包含了所有usb設(shè)備的驅(qū)動(dòng),而usb目錄下面又有它自己的子目錄,進(jìn)去看一下, localhost:/usr/src/linux-2.6.10/drivers # cd usb/ 注意到每一個(gè)目錄下面都有一個(gè)Kconfig文件和一個(gè)Makefile,這很重要.稍后會(huì)有介紹. 而我們的故事其實(shí)是圍繞著drivers/usb/storage這個(gè)目錄來(lái)展開(kāi)的.實(shí)際上這里邊的代碼清清楚楚地展示了我們?nèi)粘nl繁接觸的U盤(pán)是如何工作的,是如何被驅(qū)動(dòng)起來(lái)的.但是這個(gè)目錄里邊的冬冬并不是生活在世外桃源,他們總是和外面的世界有著千絲萬(wàn)縷的瓜葛.可以繼續(xù)進(jìn)來(lái)看一下, localhost:/usr/src/linux-2.6.10/drivers/usb # cd storage/ 咋一看,著實(shí)嚇了一跳,用`wc -l *`這個(gè)命令統(tǒng)計(jì)一下,12076行,暈死... wc [ -c | -m ] [ -l ] [ -w ] [文件] 注:缺省情況下,wc 命令對(duì) File 參數(shù)指定的文件中的行數(shù)、字?jǐn)?shù)和字節(jié)數(shù)進(jìn)行計(jì)數(shù)。這個(gè)命令將換行符數(shù)、字?jǐn)?shù)和字節(jié)數(shù)寫(xiě)到標(biāo)準(zhǔn)輸出并為所有指定的文件保留一個(gè)總數(shù)。 -c 統(tǒng)計(jì)字節(jié)數(shù),除非指定 -k 標(biāo)志。如果指定 -k 標(biāo)志,wc 命令統(tǒng)計(jì)字符數(shù)。 但是,也許,生活中總是充滿(mǎn)了跌宕起伏. 認(rèn)真看了一下Makefile和Kconfig之后,心情明顯好了許多. 出來(lái)混,遲早要還的. 從前在復(fù)旦,混了四年,沒(méi)有學(xué)到任何東西,每天就是逃課,上網(wǎng),玩游戲,睡覺(jué).畢業(yè)的時(shí)候,身邊的人讀研的讀研,出國(guó)的出國(guó),找工作的吧,去麥肯錫的去麥肯錫,去IBM的去IBM.而自己卻一無(wú)所長(zhǎng),沒(méi)有任何技能,直到這時(shí)候才發(fā)現(xiàn)那四年欠了很多債,早知今日,何必當(dāng)初.幸運(yùn)的是,我還有一張復(fù)旦的文憑,依靠著這張文憑,混進(jìn)了Intel.然而,工作以后,更是發(fā)現(xiàn)當(dāng)初在校期間沒(méi)有好好讀書(shū)其實(shí)真是在欠債,當(dāng)初沒(méi)學(xué),工作以后還是要學(xué),的確是遲早要還的,逃是逃不掉的. 畢業(yè)的時(shí)候,人家跟我說(shuō)Makefile我完全不知,但是一說(shuō)Make Love我就來(lái)勁了.現(xiàn)在想來(lái)依然覺(jué)得丟人. 基本上,Linux內(nèi)核中每一個(gè)目錄下邊都有一個(gè)Makefile,Makefile和Kconfig就像一個(gè)城市的地圖,地圖帶領(lǐng)我們?nèi)フJ(rèn)識(shí)一個(gè)城市,而Makefile和Kconfig則可以讓我們了解這個(gè)目錄下面的結(jié)構(gòu).drivers/usb/storage/目錄下邊的Makefile內(nèi)容如下: # EXTRA_CFLAGS := -Idrivers/scsi obj-$(CONFIG_USB_STORAGE) += usb-storage.o usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ 關(guān)于Kconfig文件,在故事的最后會(huì)介紹,此刻暫且不表,Kconfig文件比較長(zhǎng),就不貼出來(lái)了.但是通過(guò)看Kconfig文件,我們可以知道,除了CONFIG_USB_STORAGE這個(gè)編譯選項(xiàng)是我們真正需要的以外,別的選項(xiàng)我們都可以不予理睬.比如,關(guān)于CONFIG_USB_STORAGE_DATAFAB,Kconfig文件中有這么一段, config USB_STORAGE_DATAFAB 顯然,這個(gè)選項(xiàng)和我們沒(méi)有關(guān)系,首先這是專(zhuān)門(mén)針對(duì)Datafab公司的產(chǎn)品的,其次CompactFlash reader是一種flash設(shè)備,但這顯然不是U盤(pán),因?yàn)閐rivers/usb/storage這個(gè)目錄里邊的代碼是針對(duì)一類(lèi)設(shè)備的,不是某一種特定的設(shè)備,這一類(lèi)設(shè)備就是usb mass storage設(shè)備,關(guān)于這類(lèi)設(shè)備,有專(zhuān)門(mén)的文檔進(jìn)行介紹,有相應(yīng)的spec,描述這類(lèi)設(shè)備的通信或者物理上電特性上等方面的規(guī)范,U盤(pán)只是其中的一種,這種設(shè)備使用的通信協(xié)議被稱(chēng)為Bulk-Only Transport協(xié)議.再比如,關(guān)于CONFIG_USB_STORAGE_SDDR55這個(gè)選項(xiàng),Kconfig文件中也有對(duì)應(yīng)的一段, config USB_STORAGE_SDDR55 usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ 不過(guò)需要特別注意的是,CONFIG_USB_STORAGE_DEBUG這個(gè)編譯選項(xiàng),它不是我們必須的,但是如果真的要自己修改或者調(diào)試usb-storage的代碼,那么打開(kāi)這個(gè)選項(xiàng)是很有必要的,因?yàn)樗鼤?huì)負(fù)責(zé)打印一些調(diào)試信息,以后在源代碼中我們會(huì)看到它的作用. 有一種感動(dòng),叫淚流滿(mǎn)面,有一種機(jī)制,叫模塊機(jī)制,十月革命一聲炮響,給Linux送來(lái)了模塊機(jī)制.顯然,這種模塊機(jī)制給那些Linux的發(fā)燒友們帶來(lái)了方便,因?yàn)槟K機(jī)制意味著人們可以把龐大的Linux內(nèi)核劃分為許許多多個(gè)小的模塊,對(duì)于編寫(xiě)設(shè)備驅(qū)動(dòng)程序的那幫家伙來(lái)說(shuō),從此以后他們可以編寫(xiě)設(shè)備驅(qū)動(dòng)程序卻不需要把她編譯進(jìn)內(nèi)核,不用reboot機(jī)器,她只是一個(gè)模塊,當(dāng)你需要她的時(shí)候,你可以把她抱入懷中(insmod),當(dāng)你不再需要她的時(shí)候,你可以把她一腳踢開(kāi),甚至,你可以對(duì)她咆哮:"滾吧,賤人!"(rmmod).她不能成為你的手足,只能算你的衣服. 也許在現(xiàn)實(shí)世界里不會(huì)這樣,但是在Linux的虛擬世界里,確實(shí)可以是如此,time and time again,我問(wèn)自己,模塊是否就像現(xiàn)實(shí)生活中的妓女一樣呢?Linux內(nèi)核是嫖客,當(dāng)他需要這個(gè)模塊的時(shí)候,他就把人家攬入懷中,當(dāng)他不需要人家的時(shí)候,就把別人踢開(kāi),而且,模塊總是能夠逆來(lái)順受,盡管Linux內(nèi)核會(huì)一次次拋棄她,但是每當(dāng)Linux內(nèi)核再次需要她的時(shí)候,當(dāng)內(nèi)核再次執(zhí)行insmod的時(shí)候,模塊依然會(huì)盡自己的能力去取悅內(nèi)核,這是否太可悲了些!記得孔子曾經(jīng)說(shuō)過(guò),讀懂Linux內(nèi)核代碼不難,難得是讀懂Linux內(nèi)核代碼背后的哲學(xué)!難道這就是傳說(shuō)中的藏在Linux代碼背后的哲學(xué)!天哪! 拋開(kāi)這見(jiàn)鬼的哲學(xué)吧.讓我們從一個(gè)偉大的例子去認(rèn)識(shí)模塊.這就是傳說(shuō)中的"Hello World!",這個(gè)夢(mèng)幻般的名字我們看過(guò)無(wú)數(shù)次了,每一次她出現(xiàn)在眼前,就意味著我們開(kāi)始接觸一種新的計(jì)算機(jī)語(yǔ)言了,或者,如此刻,開(kāi)始描述一個(gè)新的故事. 請(qǐng)看下面這段代碼,她就是Linux下的一個(gè)最簡(jiǎn)單的模塊.當(dāng)你安裝這個(gè)模塊的時(shí)候,她會(huì)用她特有的語(yǔ)言向你表白,"Hello,world!",千真萬(wàn)確,她沒(méi)有說(shuō)"Honey,I love you!",雖然,她可以這么說(shuō),如果你要求她這么說(shuō).而后來(lái)你卸載了這個(gè)模塊,你無(wú)情拋棄了她,她很傷心,她很絕望,但她沒(méi)有抱怨,她只是淡淡地說(shuō),"Goodbye,cruel world!"(再見(jiàn),殘酷的世界!) ++++++++++++++++++hello.c++++++++++++++++++++ 1 #include <linux/init.h> /* Needed for the macros */ ++++++++++++++++++++++++++++++++++++++++++++++++ 你需要使用module_init()和module_exit(),你可以稱(chēng)她們?yōu)楹瘮?shù),不過(guò)實(shí)際上她們是一些宏(macro),現(xiàn)在你可以不用去知道她們背后的故事,只需要知道,在Linux Kernel 2.6的世界里,你寫(xiě)的任何一個(gè)模塊都需要使用她們來(lái)初始化或退出,或者說(shuō)注冊(cè)以及后來(lái)的注銷(xiāo).當(dāng)你用module_init()為一個(gè)模塊注冊(cè)了之后,在你使用insmod這個(gè)命令去安裝的時(shí)候,module_init()注冊(cè)的函數(shù)將會(huì)被執(zhí)行,而當(dāng)你用rmmod這個(gè)命令去卸載一個(gè)模塊的時(shí)候,module_exit()注冊(cè)的函數(shù)將會(huì)被執(zhí)行.module_init()被稱(chēng)為驅(qū)動(dòng)程序的初始化入口(driver initialization entry point). 怎么樣演示以上代碼的運(yùn)行呢?沒(méi)錯(cuò),你需要一個(gè)Makefile. +++++++++++++++++++++Makefile+++++++++++++++++++++++++++ 1 # To build modules outside of the kernel tree, we run "make" 在lwn上可以找到這個(gè)例子,你可以把以上兩個(gè)文件放在你的某個(gè)目錄下,然后執(zhí)行make,也許你不一定能成功,因?yàn)?/span>LK 2.6要求你編譯模塊之前,必須先在內(nèi)核源代碼目錄下執(zhí)行make,換言之,你必須先配置過(guò)內(nèi)核,執(zhí)行過(guò)make,然后才能make你自己的模塊.原因我就不細(xì)說(shuō)了,你按著她要求的這么去做就行了.在內(nèi)核頂層目錄make過(guò)之后,你就可以在你當(dāng)前放置Makefile的目錄下執(zhí)行make了.Ok,make之后你就應(yīng)該看到一個(gè)叫做hello.ko的文件生成了,恭喜你,這就是你將要測(cè)試的模塊. 執(zhí)行命令, #insmod hello.ko 同時(shí)在另一個(gè)窗口,用命令tail -f /var/log/messages察看日志文件,你會(huì)看到 Hello world被打印了出來(lái). 再執(zhí)行命令, #rmmod hello.ko 此時(shí),在另一窗口你會(huì)看到Goodbye,cruel world!被打印了出來(lái). 到這里,我該恭喜你,因?yàn)槟阋呀?jīng)能夠編寫(xiě)Linux內(nèi)核模塊了.這種感覺(jué)很美妙,不是嗎?你可以嘲笑秦皇漢武略輸文采唐宗宋祖稍遜風(fēng)騷,還可以嘲笑一代天驕成吉思汗只識(shí)彎弓射大雕了. 是的,Twins姐姐(s)告訴我們,只要我喜歡,還有什么不可以. 日后我們會(huì)看到,2.6內(nèi)核中,每個(gè)模塊都是以module_init開(kāi)始,以module_exit結(jié)束.大多數(shù)來(lái)說(shuō)沒(méi)有必要知道這是為什么,記住就可以了,相信每一個(gè)對(duì)Linux有一點(diǎn)常識(shí)的人都會(huì)知道這一點(diǎn)的,對(duì)大多數(shù)人來(lái)說(shuō),這就像是1+1為什么等于2一樣,就像是兩點(diǎn)之間最短的是直線,不需要證明,如果一定要證明兩點(diǎn)之間直線最短,可以扔一塊骨頭在B點(diǎn),讓一條狗從A點(diǎn)出發(fā),你會(huì)發(fā)現(xiàn)狗走的是直線,是的,狗都知道,你還能不知道嗎?
既然知道了怎么編寫(xiě)一個(gè)模塊,那么編寫(xiě)設(shè)備驅(qū)動(dòng)程序自然也就不難了.我相信,每一個(gè)會(huì)寫(xiě)模塊的人都不會(huì)覺(jué)得寫(xiě)設(shè)備驅(qū)動(dòng)有困難.對(duì)自己行不行不確定的話(huà),可以去問(wèn)一下葛優(yōu),他準(zhǔn)說(shuō):"(神州行),我看行." 真的,我沒(méi)說(shuō)假話(huà).寫(xiě)驅(qū)動(dòng)不是什么難事,你完全可以很自信的說(shuō),你已經(jīng)可以寫(xiě)Device Driver了.對(duì),沒(méi)錯(cuò),飄柔,就這么自信. 前面說(shuō)了每一個(gè)模塊都是以module_init開(kāi)始,以module_exit結(jié)束,那么我們就來(lái)看一下U盤(pán)的驅(qū)動(dòng)的這個(gè)模塊.在茫茫人海中,我們很容易找到這個(gè)文件:drivers/usb/storage/usb.c,在這個(gè)文件中又不難發(fā)現(xiàn)下面這段: /*********************************************************************** 其實(shí),module_init/module_exit只是一個(gè)宏,通常寫(xiě)模塊的人為了彰顯自己的個(gè)性,會(huì)給自己的初始化函數(shù)和注銷(xiāo)函數(shù)另外起個(gè)名字,比如這里module_init(usb_stor_init)以及module_exit(usb_stor_exit)實(shí)際上就是告訴這個(gè)世界,真正的函數(shù)是usb_stor_init和usb_stor_exit.這種伎倆在Linux內(nèi)核代碼中屢見(jiàn)不鮮.見(jiàn)多了也就不必大驚小怪了,天要下雨娘要嫁人,隨她去吧.我們下面當(dāng)然就從usb_stor_init正式開(kāi)始我們的探索之旅.
看代碼之前,我曾經(jīng)認(rèn)真的思考過(guò)這么一個(gè)問(wèn)題,我需要關(guān)注的僅僅是drivers/usb/storage/目錄下面那相關(guān)的3000多行代碼嗎?就是這樣幾個(gè)文件就能讓一個(gè)個(gè)不同的U盤(pán)在Linux下面工作起來(lái)嗎? 像一開(kāi)始那樣把這個(gè)目錄比作一個(gè)小城的話(huà),也許,城里的月光很漂亮,她能夠把人的夢(mèng)照亮,能夠溫暖人的心房.但我們真的就能廝守在這個(gè)城里,一生一世嗎? 很不幸,問(wèn)題遠(yuǎn)不是這樣簡(jiǎn)單.外面的世界很精彩,作為U盤(pán),她需要與usb core打交道,需要與scsi core打交道,需要與內(nèi)存管理單元打交道,還有內(nèi)核中許許多多其它模塊打交道.外面的世界很大,遠(yuǎn)比我們想象的大. 什么是usb core?她負(fù)責(zé)實(shí)現(xiàn)一些核心的功能,為別的設(shè)備驅(qū)動(dòng)程序提供服務(wù),比如申請(qǐng)內(nèi)存,比如實(shí)現(xiàn)一些所有的設(shè)備都會(huì)需要的公共的函數(shù),事實(shí)上,在usb的世界里,一個(gè)普通的設(shè)備要正常的工作,除了要有設(shè)備本身以外,還需要有一個(gè)叫做控制器的冬冬,老外把它叫做host controller,和這個(gè)控制器相連接在一起的有另一個(gè)咚咚,她叫root hub,hub我們應(yīng)該不會(huì)陌生,在大學(xué)里,有的宿舍里網(wǎng)口有限,但是我們這一代人上大學(xué)基本上是每人一臺(tái)電腦,所以網(wǎng)口不夠,于是有人會(huì)使用hub,讓多個(gè)人共用一個(gè)網(wǎng)口,這是以太網(wǎng)上的hub,而usb的世界里同樣有hub,其實(shí)原理是一樣的,任何支持usb的電腦不會(huì)說(shuō)只允許你只能一個(gè)時(shí)刻使用一個(gè)usb設(shè)備,比如你插入了u盤(pán),你同樣還可以插入usb鍵盤(pán),還可以再插一個(gè)usb鼠標(biāo),因?yàn)槟銜?huì)發(fā)現(xiàn)你的電腦里并不只是一個(gè)usb接口.這些口實(shí)際上就是所謂的hub口.而現(xiàn)實(shí)中經(jīng)常是讓一個(gè)usb控制器和一個(gè)hub綁定在一起,專(zhuān)業(yè)一點(diǎn)說(shuō)叫集成,而這個(gè)hub也被稱(chēng)作root hub,換言之,和usb控制器綁定在一起的hub就是系統(tǒng)中最根本的hub,其它的hub可以連接到她這里,然后可以延伸出去,外接別的設(shè)備,當(dāng)然也可以不用別的hub,讓usb設(shè)備直接接到root hub上.hub干嘛用的我們知道了,那么usb host controller本身是干什么用的呢?controller,控制器,顧名思義,用于控制,控制什么,控制所有的usb設(shè)備的通信.通常計(jì)算機(jī)的cpu并不是直接和usb設(shè)備打交道,而是和控制器打交道,他要對(duì)設(shè)備做什么,他會(huì)告訴控制器,而不是直接把指令發(fā)給設(shè)備,然后控制器再去負(fù)責(zé)處理這件事情,他會(huì)去指揮設(shè)備執(zhí)行命令,而cpu就不用管剩下的事情,他還是該干嘛干嘛去,控制器替他去完成剩下的事情,事情辦完了再通知cpu.否則讓cpu去盯著每一個(gè)設(shè)備做每一件事情,那是不現(xiàn)實(shí)的,那就好比讓一個(gè)學(xué)院的院長(zhǎng)去盯著我們每一個(gè)本科生上課,去管理我們的出勤,只能說(shuō),不現(xiàn)實(shí).所以我們就被分成了幾個(gè)系,通常院長(zhǎng)有什么指示直接跟各系領(lǐng)導(dǎo)說(shuō)就可以了,如果他要和三個(gè)系主任說(shuō)事情,他即使不把三個(gè)人都召集起來(lái)開(kāi)個(gè)會(huì),也可以給三個(gè)人各打一個(gè)電話(huà),打完電話(huà)他就忙他自己的事情去了,比如去和他帶的女碩士風(fēng)花雪月.而三個(gè)系主任就會(huì)去安排下面的人去執(zhí)行具體的任務(wù),完了之后他們就會(huì)像院長(zhǎng)匯報(bào). 所以,Linux內(nèi)核開(kāi)發(fā)者們,專(zhuān)門(mén)寫(xiě)了一些代碼,并美其名曰usb core.時(shí)代總在發(fā)展,當(dāng)年胖楊貴妃照樣迷死唐明皇,而如今人們欣賞的則是林志玲這樣的魔鬼身材.同樣,早期的Linux內(nèi)核,其結(jié)構(gòu)并不是如今天這般有層次感,遠(yuǎn)不像今天這般錯(cuò)落有致,那時(shí)候drivers/usb/這個(gè)目錄下邊放了很多很多文件,usb core與其他各種設(shè)備的驅(qū)動(dòng)程序的代碼都堆砌在這里,后來(lái),怎奈世間萬(wàn)千的變幻,總愛(ài)把有情的人分兩端.于是在drivers/usb/目錄下面出來(lái)了一個(gè)core目錄,就專(zhuān)門(mén)放一些核心的代碼,比如初始化整個(gè)usb系統(tǒng),初始化root hub,初始化host controller的代碼,再后來(lái)甚至把host controller相關(guān)的代碼也單獨(dú)建了一個(gè)目錄,叫host目錄,這是因?yàn)閡sb host controller隨著時(shí)代的發(fā)展,也開(kāi)始有了好幾種,不再像剛開(kāi)始那樣只有一種,所以呢,設(shè)計(jì)者們把一些host controller公共的代碼仍然留在core目錄下,而一些各host controller單獨(dú)的代碼則移到host目錄下面讓負(fù)責(zé)各種host controller的人去維護(hù),常見(jiàn)的host controller有三種,分別叫做EHCI,UHCI,OHCI,所以這樣,出來(lái)了三個(gè)概念,usb core,usb host,usb device,即原本是一家人,卻被活生生的分成了兩岸三地...的確,現(xiàn)實(shí)總是很無(wú)奈,然而,心若知道靈犀的方向,哪怕不能夠朝夕相伴?沒(méi)錯(cuò),usb通信的靈魂就是usb協(xié)議. usb協(xié)議將是所有usb設(shè)備和usb主機(jī)所必須遵循的游戲規(guī)則.這種規(guī)則也很自然的體現(xiàn)在了代碼中.于是,我們需要了解的不僅僅是drivers/usb/storage/目錄下面的冬冬,還得去了解那外面的世界,雖然,只需要了解一點(diǎn)點(diǎn). 還是回到那個(gè)初始化函數(shù)吧,usb_stor_init,看了它的代碼每一個(gè)人的心中都有一種莫名的興奮,因?yàn)樗塘?就那么幾行,除了兩個(gè)printk語(yǔ)句以外,就是一個(gè)函數(shù)的調(diào)用,usb_register. printk不用我說(shuō),每一個(gè)有志青年都該知道,就算沒(méi)見(jiàn)過(guò)printk也該見(jiàn)過(guò)printf吧,否則的話(huà),你捫心自問(wèn),你對(duì)得起譚浩強(qiáng)大哥嗎?在譚浩強(qiáng)大哥的帶領(lǐng)下我們學(xué)會(huì)了用#include<stdio.h>->main()->printf()來(lái)打印hello,world!從而向全世界展示了我們懂C語(yǔ)言.而stdio.h就是一個(gè)C庫(kù),printf是一個(gè)函數(shù),來(lái)自函數(shù)庫(kù),可是內(nèi)核中沒(méi)有標(biāo)準(zhǔn)C庫(kù),所以開(kāi)發(fā)者們自己準(zhǔn)備了一些函數(shù),專(zhuān)門(mén)用于內(nèi)核代碼中,所以就出來(lái)了一個(gè)printk,printk的"k"就是kernel,內(nèi)核.所以我們只要把它當(dāng)作printf的兄弟即可,如果感興趣,可以去研究一下printk的特點(diǎn),她和printf多少有些不同,但基本思想是一樣的.所以我們就不多講了,當(dāng)然驅(qū)動(dòng)程序中所有的printk語(yǔ)句對(duì)U盤(pán)的工作都沒(méi)有什么用,她無(wú)非是打出來(lái)給我們看的,或者說(shuō)打印給用戶(hù)看,或者呢,打印給開(kāi)發(fā)者看,特別是開(kāi)發(fā)者要調(diào)試程序的時(shí)候,就會(huì)很有用. 于是我們更開(kāi)心了,不用看printk的話(huà),那就只有一個(gè)函數(shù)調(diào)用了,usb_register.這個(gè)函數(shù)是干嘛的?首先這個(gè)函數(shù)正是來(lái)自u(píng)sb core.凡是usb設(shè)備驅(qū)動(dòng),都要調(diào)用這個(gè)函數(shù)來(lái)向usb core注冊(cè),從而讓usb core知道有這么一個(gè)設(shè)備.這就像政府規(guī)定,一對(duì)夫妻結(jié)婚要到相關(guān)部門(mén)那里去登記是一樣的,我們無(wú)需知道政府是如何管理的,只需要知道去政府那里登記即可. 這樣,insmod的時(shí)候,usb_stor_init這個(gè)函數(shù)會(huì)被調(diào)用,初始化就算完成了.于是設(shè)備就開(kāi)始工作了...而當(dāng)我們r(jià)mmod的時(shí)候,usb_stor_exit這個(gè)函數(shù)會(huì)被調(diào)用,我們發(fā)現(xiàn),這個(gè)函數(shù)也很短,我們能看出來(lái),US_DEBUG也就是打印一些咚咚,因此,這里實(shí)際上也就是調(diào)用了一個(gè)函數(shù)usb_deregister(),她和usb_register()是一對(duì),完成了注銷(xiāo)的工作,從此設(shè)備就從usb core中消失了.于是我們驚人的發(fā)現(xiàn),編寫(xiě)設(shè)備驅(qū)動(dòng)竟是如此的簡(jiǎn)單,驅(qū)動(dòng)程序真的就這么結(jié)束了?... 這一切,不禁讓人產(chǎn)生了一種幻覺(jué),讓人分不清故事從哪里開(kāi)始,又從哪里結(jié)束,一切都太短暫了.仿佛開(kāi)始在結(jié)束的時(shí)候開(kāi)始,而結(jié)束卻在開(kāi)始的時(shí)候就早已結(jié)束. 真的嗎? 答案是否定的.孔子已經(jīng)教育過(guò)我們,不光要看懂代碼,更要理解代碼背后的哲學(xué). 所以我們?cè)诶^續(xù)之前,先來(lái)看看這里到底有什么哲學(xué).而這,就是偉大的Linux Kernel 2.6中的統(tǒng)一的設(shè)備模型. 我們并無(wú)意去詳細(xì)介紹2.6中的設(shè)備模型,但是不懂設(shè)備模型又怎能說(shuō)自己懂設(shè)備驅(qū)動(dòng)呢?讀代碼的人,寫(xiě)代碼的人,都要知道,什么是設(shè)備驅(qū)動(dòng)?什么又是設(shè)備?設(shè)備和驅(qū)動(dòng)之間究竟是什么關(guān)系?設(shè)備如何與計(jì)算機(jī)主機(jī)聯(lián)系起來(lái)?我相信在中關(guān)村買(mǎi)盜版光盤(pán)的哥們兒也能回答這個(gè)問(wèn)題.計(jì)算機(jī)世界里,設(shè)備有很多種類(lèi),比如PCI設(shè)備,比如ISA設(shè)備,再比如SCSI設(shè)備,再比如我們這里的USB設(shè)備.為設(shè)備聯(lián)姻的是總線,是他把設(shè)備連入了計(jì)算機(jī)主機(jī).但是與其說(shuō)設(shè)備是嫁給了計(jì)算機(jī)主機(jī),倒不如說(shuō)設(shè)備是嫁給了設(shè)備驅(qū)動(dòng)程序.很顯然,在計(jì)算機(jī)世界里,無(wú)論風(fēng)里雨里,陪伴著設(shè)備的正是驅(qū)動(dòng)程序. 唯一的遺憾是,計(jì)算機(jī)中的設(shè)備和驅(qū)動(dòng)程序的關(guān)系卻并非如可樂(lè)和拉環(huán)的關(guān)系那樣,一對(duì)一.然而世上又有多少事情總能如人愿呢. Linux設(shè)備模型中三個(gè)很重要的概念就是總線,設(shè)備,驅(qū)動(dòng).即bus,device,driver,而實(shí)際上內(nèi)核中也定義了這么一些數(shù)據(jù)結(jié)構(gòu),他們是struct bus_type,struct device,struct device_driver,這三個(gè)重要的數(shù)據(jù)結(jié)構(gòu)都來(lái)自一個(gè)地方,include/linux/device.h.我們知道總線有很多種,pci總線,scsi總線,usb總線,所以我們會(huì)看到Linux內(nèi)核代碼中出現(xiàn)pci_bus_type,scsi_bus_type,usb_bus_type,他們都是struct bus_type類(lèi)型的變量.而struct bus_type結(jié)構(gòu)中兩個(gè)非常重要的成員就是struct kset drivers和struct kset devices.kset和另一個(gè)叫做kobject正是Linux Kernel 2.6中設(shè)備模型的基本元素,但此處我們卻不愿多講,因?yàn)闀簳r(shí)不用去認(rèn)識(shí)他們.我們的生命中會(huì)遇見(jiàn)許許多多的人和事,但更多的人和事與我們只是擦肩而過(guò),只是我們生命中的過(guò)客而已.在我們?nèi)松碾娪爸?他們也許只有一個(gè)鏡頭,甚至那一個(gè)鏡頭后來(lái)也被剪輯掉了.這里我們只需要知道,drivers和devices的存在,讓struct bus_type與兩個(gè)鏈表聯(lián)系了起來(lái),一個(gè)是devices的鏈表,一個(gè)是drivers的鏈表,也就是說(shuō),知道一條總線所對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),就可以找到這條總線所關(guān)聯(lián)的設(shè)備有哪些,又有哪些支持這類(lèi)設(shè)備的驅(qū)動(dòng)程序. 而要實(shí)現(xiàn)這些,就要求每次出現(xiàn)一個(gè)設(shè)備就要向總線匯報(bào),或者說(shuō)注冊(cè),每次出現(xiàn)一個(gè)驅(qū)動(dòng),也要向總線匯報(bào),或者說(shuō)注冊(cè).比如系統(tǒng)初始化的時(shí)候,會(huì)掃描連接了哪些設(shè)備,并為每一個(gè)設(shè)備建立起一個(gè)struct device的變量,每一次有一個(gè)驅(qū)動(dòng)程序,就要準(zhǔn)備一個(gè)struct device_driver結(jié)構(gòu)的變量.把這些變量統(tǒng)統(tǒng)加入相應(yīng)的鏈表,device插入devices鏈表,driver插入drivers鏈表. 這樣通過(guò)總線就能找到每一個(gè)設(shè)備,每一個(gè)驅(qū)動(dòng). 然而,假如計(jì)算機(jī)里只有設(shè)備卻沒(méi)有對(duì)應(yīng)的驅(qū)動(dòng),那么設(shè)備無(wú)法工作.反過(guò)來(lái),倘若只有驅(qū)動(dòng)卻沒(méi)有設(shè)備,驅(qū)動(dòng)也起不了任何作用.在他們遇見(jiàn)彼此之前,雙方都如同路埂的野草,一個(gè)飄啊飄,一個(gè)搖啊搖,誰(shuí)也不知道未來(lái)在哪里,只能在生命的風(fēng)里飄搖.于是總線上的兩張表里就慢慢的就掛上了那許多孤單的靈魂.devices開(kāi)始多了,drivers開(kāi)始多了,他們像是兩個(gè)來(lái)自世界,devices們彼此取暖,drivers們一起狂歡,但他們有一點(diǎn)是相同的,都只是在等待屬于自己的那個(gè)另一半. 看代碼的我,一直好奇的想知道,他們是否和我們現(xiàn)實(shí)中一樣,有些人注定是等別人,而有些人是注定被人等的. struct bus_type中為devices和drivers準(zhǔn)備了兩個(gè)鏈表,而代表device的結(jié)構(gòu)體struct device中又有兩個(gè)成員,struct bus_type *bus和struct device_driver *driver,同樣,代表driver的結(jié)構(gòu)體struct device_driver同樣有兩個(gè)成員,struct bus_type *bus和struct list_head devices,struct device和struct device_driver的定義和struct bus_type一樣,在include/linux/device.h中.憑一種男人的直覺(jué),可以知曉,struct device中的bus記錄的是這個(gè)設(shè)備連在哪條總線上,driver記錄的是這個(gè)設(shè)備用的是哪個(gè)驅(qū)動(dòng),反過(guò)來(lái),struct device_driver中的bus代表的也是這個(gè)驅(qū)動(dòng)屬于哪條總線,devices記錄的是這個(gè)驅(qū)動(dòng)支持的那些設(shè)備,沒(méi)錯(cuò),是devices(復(fù)數(shù)),而不是device(單數(shù)),因?yàn)橐粋€(gè)驅(qū)動(dòng)程序可以支持一個(gè)或多個(gè)設(shè)備,反過(guò)來(lái)一個(gè)設(shè)備則只會(huì)綁定給一個(gè)驅(qū)動(dòng)程序.
那么drivers鏈表呢?這個(gè)就不用bus方面主動(dòng)了,而該由每一個(gè)driver本身去bus上面登記,或者說(shuō)掛牌.具體到usb系統(tǒng),每一個(gè)usb設(shè)備的驅(qū)動(dòng)程序都會(huì)有一個(gè)struct usb_driver結(jié)構(gòu)體,其代碼如下,來(lái)自include/linux/usb.h 485 /* -------------------------------------------------------------------------- */
|
|