linux设备驱动——andriod平台wlan驱动
轉(zhuǎn)自 :http://blog.chinaunix.net/space.php?uid=22278460&do=blog&cuid=2186191
linux設備驅(qū)動——andriod平臺wlan驅(qū)動
關于這一部分的blog,所有的內(nèi)容均摘自自己的工作總結筆記,在很多網(wǎng)站都發(fā)現(xiàn)了自己寫的技術總結的轉(zhuǎn)載感到很高興,雖然我還是個菜鳥,但是我會繼續(xù)努力。另外關于wlan驅(qū)動這方面的資料真的很少,我基本上是靠自己讀代碼來理解那些繁瑣的寄存器讀寫、802.11/e/h/d等標準的,真的比較辛苦。不過好在算是慢慢的搞清楚了這個流程,在此之前我們?nèi)匀灰a習一下關于在2.6版本內(nèi)核中寫驅(qū)動的知識。
??? 有關linux設備模型這一塊比較復雜,我不敢斷定自己理解的肯定正確,但是我會在做這個驅(qū)動的過程中回過頭來修改自己的筆記并且糾正自己在blog上貼的并不正確的地方。另外,我的無線網(wǎng)卡是掛接在SDIO總線上的,所以呢,我們之前會先介紹一點SDIO的驅(qū)動,當然并不在這篇blog上,這篇blog會是總領性的關于基礎知識的介紹。下面是筆記
?
在進入正式的驅(qū)動代碼之前,我們不得不補充一點基礎知識,也就是在2.6版本內(nèi)核的現(xiàn)在,內(nèi)核是如何管理總線,驅(qū)動,設備之間的關系的,關于bus_type、device_driver、device這三個內(nèi)核結構在內(nèi)核代碼中可以找到。由于這三個結構的重要性,我們在這里先將它們貼出來,我會用紅色的注釋標注。我在筆記中將會提到的一些結構成員以其代表的意義,這樣便于對照。(我不得不承認,這三個結構的代碼枯燥復雜,但我可以保證,在看完我總結的筆記之后,你會對這三個結構中重要的數(shù)據(jù)成員有個非常不錯的了解,當然你得對內(nèi)核和驅(qū)動有基本的了解。如果我誤導了你,請指正我,所以我的建議是不妨先看完了筆記再來看這些結構)
?
1、設備結構的定義:
struct device {
?struct klist??klist_children;????????
?struct klist_node?knode_parent;??/* node in sibling list */
?struct klist_node?knode_driver;
?struct klist_node?knode_bus;
?struct device??*parent;
struct kobject kobj;???? //kobject結構,關于這個結構與kset結構以及subsystem結構,筆記中會有描述。
?char?bus_id[BUS_ID_SIZE];?/* position on parent bus */
?struct device_type?*type;
?unsigned??is_registered:1;
?unsigned??uevent_suppress:1;
struct semaphore?sem;?/* semaphore to synchronize calls to
* its driver.
????? */
struct bus_type?* bus;??/* type of bus device is on */??//這個設備掛接的總線的類型
?struct device_driver *driver;?/* which driver has allocated this device */??//這個設備掛接的驅(qū)動
?void??*driver_data;?/* data private to the driver */
void??*platform_data;?/* Platform specific data, device core doesn't touch it */
?struct dev_pm_info?power;
#ifdef CONFIG_NUMA
?int??numa_node;?/* NUMA node this device is close to */
#endif
u64??*dma_mask;?/* dma mask (if dma'able device) */
?u64??coherent_dma_mask;/* Like dma_mask, but for
????????? alloc_coherent mappings as
????????? not all hardware supports
????????? 64 bit addresses for consistent
????????? allocations such descriptors. */
struct list_head?dma_pools;?/* dma pools (if dma'ble) */
struct dma_coherent_mem?*dma_mem; /* internal for coherent mem override */
?/* arch specific additions */
?struct dev_archdata?archdata;
spinlock_t??devres_lock;
?struct list_head?devres_head;
/* class_device migration path */
?struct list_head?node;
?struct class??*class;
?dev_t???devt;??/* dev_t, creates the sysfs "dev" */
?struct attribute_group?**groups;?/* optional groups */
void?(*release)(struct device * dev);
};
2、設備驅(qū)動的結構:
struct device_driver {
?const char??* name;???????????????????????????????? //設備驅(qū)動的名字
?struct bus_type??* bus;?????????????????????????????//設備驅(qū)動掛接的總線的類型
struct kobject??kobj;???????????????????????????????? //kobject結構
struct klist??klist_devices;???????????????????????????//這個驅(qū)動對應的設備的鏈表
?struct klist_node?knode_bus;
struct module??* owner;
?const char ??* mod_name;?/* used for built-in modules */
struct module_kobject?* mkobj;
int?(*probe)?(struct device * dev);
?int?(*remove)?(struct device * dev);
?void?(*shutdown)?(struct device * dev);
int?(*suspend)?(struct device * dev, pm_message_t state);
?int?(*resume)?(struct device * dev);
};
?
3、總線結構:
struct bus_type {
?const char??* name;?????????????????????????????????//總線的名字
?struct module??* owner;
struct kset??subsys;??????????????????????????????? //與該總線相關的subsystem
struct kset??drivers;???????????????????????????????//掛接在該總線上的驅(qū)動的集合
?struct kset??devices;?????????????????????????????? //掛接在該總線上的設備的集合
?struct klist??klist_devices;????????????????????????
?struct klist??klist_drivers;
struct blocking_notifier_head bus_notifier;
struct bus_attribute?* bus_attrs;????????????????? //總線屬性
?struct device_attribute?* dev_attrs;???????????????//設備屬性
?struct driver_attribute?* drv_attrs;?????????????? //驅(qū)動屬性
int??(*match)(struct device * dev, struct device_driver * drv);
int??(*uevent)(struct device *dev, struct kobj_uevent_env *env);
?int??(*probe)(struct device * dev);
?int??(*remove)(struct device * dev);
?void??(*shutdown)(struct device * dev);
int (*suspend)(struct device * dev, pm_message_t state);
?int (*suspend_late)(struct device * dev, pm_message_t state);
?int (*resume_early)(struct device * dev);
?int (*resume)(struct device * dev);
unsigned int drivers_autoprobe:1;
};
?
我們注意到只有在bus_type結構中有kset結構,其他兩個結構中則沒有,我們知道kset結構是用于存放相同類型的kobject的,這究竟是個什么意思呢?kset又是為什么而存在的呢?為什么不能就是kobject呢?(關于kobject結構,我們很難抽象的形容,盡管它就是一個抽象的概念,我們將留待看代碼的時候介紹,這里可以將kobject看成一個基類,kset就是容器了),下面我會按照自己的理解來回答這個問題(建議通讀一下《linux設備驅(qū)動第三版》的linux 設備模型章節(jié))
?? 首先不管是設備還是驅(qū)動,都是掛接在某條總線上的,也就是說我們根據(jù)總線類型的不同來區(qū)分各種設備和驅(qū)動。(但是我們也要注意到,一個設備和驅(qū)動是可以掛接在不同的總線上的,比如網(wǎng)卡可以掛接在pci和sdio總線上,但這并不是說在linux設備模型中就可以同時掛接在兩個總線上,我們只能選擇其中的一種掛接)。
?? 在內(nèi)核代碼中我們找到了bus_type結構,我們發(fā)現(xiàn)了它使用了三個kset結構,分別是:?????? struct kset? subsys? 、struct kset? drivers 、struct kset devices。我們先拋開subsys因為它是用來向上掛接的。這里我們先看drivers與devices兩個kset結構的意義。我們從發(fā)現(xiàn)一個設備或者驅(qū)動說起吧,一個設備或者驅(qū)動向內(nèi)核注冊的時候(對于設備來說就是被插入了;對于驅(qū)動來說就是.ko模塊被加載了),對于每一次設備注冊或者驅(qū)動注冊,我們都得分配一個device結構或者device_drive結構,每一次我們都需要將device結構掛入drivers或devices(kset結構)鏈表中,這樣我們能通過總線找到掛接在這個總線上的所有設備和驅(qū)動。但是這里我們注意到僅僅將設備們和驅(qū)動們掛接在總線上并不能表明設備和驅(qū)動之間的關系,這樣的處理僅僅表明了驅(qū)動、設備與總線的關系,它們申明了我現(xiàn)在掛接在這條總線下,以后操作我就請通過這條總線。
那么設備如何認出驅(qū)動,或者驅(qū)動如何認出設備呢?是的,我們是使用的probe函數(shù)。那么需要完成哪些過程了?在這些結構總是如何關聯(lián)的?這才是我們關心的問題。所以這里我們將不得不在說明一下klist結構的作用。在內(nèi)核代碼中我們再次找到了device結構和device_drive結構。我們注意到在device結構中存在一個struct device_driver *driver這樣的聲明,而在device_drive中卻并沒有同樣的包含device結構。我們這樣想就明白了:對于一個設備來說,我們只能綁定一個驅(qū)動;而對于一個驅(qū)動來說,我們是可以對應于多個設備的。也就是說這里device中的driver指針將會指向其綁定的驅(qū)動。那么回到probe探測函數(shù),在我們對一個設備驅(qū)動進行注冊的過程中,我們會在其相應的總線(也就是其掛接的總線)上發(fā)出一個探測,這個探測會搜尋所有掛接在這個總線上的尚未被綁定的設備(也就是driver指針為NULL),然后將driver指針指向這個驅(qū)動的結構,同時將這個設備的device結構掛接在device_driver結構中的klist鏈表中。 另外要提及一點就是我居然發(fā)現(xiàn)在device結構中也發(fā)現(xiàn)了一個這樣的結構struct klist_node??? knode_driver,它看起來跟klist有關,但是我得說我確實不認為device需要一個klist來掛上它能使用的driver。同樣,當一個設備被注冊時,它也會去尋找掛接在同一條總線上的驅(qū)動,并將自己與這個驅(qū)動聯(lián)系起來。
?
?? 關于基礎知識大致就這么些,如果需要知道的更細,可以查看《linux設備驅(qū)動第三版》,后面的我們會結合代碼敘述注冊設備,發(fā)現(xiàn)設備(對于驅(qū)動來說這里的工作其實很少,如果想知道內(nèi)核做了些什么,就請跟著我一起看看內(nèi)核的奧秘),以及數(shù)據(jù)傳輸?shù)尿?qū)動代碼。
?
sdio_register_driver(&sdio) 函數(shù)被調(diào)用從而注冊sdio驅(qū)動.這里已經(jīng)進入內(nèi)核部分代碼,他存在于內(nèi)核的drivers/mmc/core/sdio_bus.c文件中,噢,忘了說了,我看的內(nèi)核代碼版本是2.6.30.1.
貼一下sdio_register_driver函數(shù):???
int sdio_register_driver(struct sdio_driver *drv)
{
??????? drv->drv.name = drv->name;?? //首先忽略下面兩行,直接進入driver_register函數(shù).
??????? drv->drv.bus = &sdio_bus_type;???//實際上這行代碼是關鍵,而下面的函數(shù)中我們要找的僅僅是調(diào)用probe函數(shù)的地方而已,稍后分析
?????? return driver_register(&drv->drv);
}
?
來看driver_register函數(shù)的內(nèi)容,由于其中涉及較多有關klist、kobject結構的內(nèi)容,如果有不明白的地方,請查看同為《linux設備驅(qū)動——andriod平臺wlan驅(qū)動》系列的介紹klist、kobject內(nèi)容的章節(jié),這里希望有的放矢.它的代碼在drivers/base/driver.c中.
int driver_register(struct device_driver *drv)
{
??????? int ret;
??????? struct device_driver *other;
??????? if ((drv->bus->probe && drv->probe) ||
??????????? (drv->bus->remove && drv->remove) ||
??????????? (drv->bus->shutdown && drv->shutdown))
??????????????? printk(KERN_WARNING "Driver '%s' needs updating - please use "
??????????????????????? "bus_type methods\n", drv->name);
??????? other = driver_find(drv->name, drv->bus);??????? //在kobject結構組成的鏈表中查找是否已經(jīng)存在這個驅(qū)動,以前的blog講過,驅(qū)動必然掛接在某個總線上.請復習,返回值是device_driver結構的指針
??????? if (other) {
??????????????? put_driver(other);??????????????? //由于之前增加了引用計數(shù),這里在減1 ??????????????????????
??????????????? printk(KERN_ERR "Error: Driver '%s' is already registered, "
??????????????????????? "aborting...\n", drv->name);
??????????????? return -EEXIST;
??????? }
?????? ret = bus_add_driver(drv);???????????? //此函數(shù)是重點!
??????? if (ret)
??????????????? return ret;
??????? ret = driver_add_groups(drv, drv->groups);?????????????? //這兩個函數(shù)不用介紹也猜的出來
??????? if (ret)
??????????????? bus_remove_driver(drv);
??????? return ret;
}
?
那么我們接著看bus_add_driver函數(shù),這個函數(shù)的作用是:如果驅(qū)動還未掛接在總線上,掛接它并且調(diào)用probe函數(shù)進行探測.它的代碼在drivers/base/bus.c中.
int bus_add_driver(struct device_driver *drv)???????????? //這個函數(shù)我不記得我的blog中是否有講過(我記得我寫過注釋,如果沒寫過,就請自己看吧? 呵呵 )
{
?????? .......................................
??????? if (drv->bus->p->drivers_autoprobe) {
??????????????? error = driver_attach(drv);?????????????????????????? //這個函數(shù)是重點.
??????????????? if (error)
??????????????????????? goto out_unregister;
??????? }
? ? ? ? ...................................... ?????
?}
driver_attach函數(shù)在drivers/base/dd.c中,很簡單的一句話:
?int driver_attach(struct device_driver *drv)
{
??????? return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
這個函數(shù)會調(diào)用__driver_attach函數(shù),我們已經(jīng)接近目標了.
static int __driver_attach(struct device *dev, void *data)
{
??????? struct device_driver *drv = data;
??????? /*
??????? * Lock device and try to bind to it. We drop the error
???????? * here and always return 0, because we need to keep trying
???????? * to bind to devices and some drivers will return an error
???????? * simply if it didn't support the device.
???????? *
???????? * driver_probe_device() will spit a warning if there
???????? * is an error.
???????? */
??????? if (dev->parent)??????? /* Needed for USB */
??????????????? down(&dev->parent->sem);
??????? down(&dev->sem);
??????? if (!dev->driver)
??????????????? driver_probe_device(drv, dev);????????????????????? //此函數(shù)就是我們要找的函數(shù)了.
?????? up(&dev->sem);
??????? if (dev->parent)
??????????????? up(&dev->parent->sem);
??????? return 0;
}
?
driver_probe_device函數(shù)中有一個really_probe函數(shù),這是我們的最終目的地:
static int really_probe(struct device *dev, struct device_driver *drv)
{
??????? ...................................................
?????????? if (dev->bus->probe) {???????????????????????????????????????? //我們看到了這里會調(diào)用probe函數(shù),同樣這里也會決定你的probe函數(shù)是使用一個參數(shù)還是兩個參數(shù),
??????????????? ret = dev->bus->probe(dev);????????????????????????? 因為這取決于你是如何注冊的.也就是是否調(diào)用了(僅僅針對于網(wǎng)友的提問)platform_device_register
?????????????? if (ret)??????????????????????????????????????????????????????????????? 函數(shù)來注冊設備.具體看下面的總結.
??????????????????????? goto probe_failed;
??????? } else if (drv->probe) {
?????????????? ret = drv->probe(dev);
??????????????? if (ret)
??????????????????????? goto probe_failed;
??????? }
???????? ....................................................
}
好了,這里來總結一下得失,同時根據(jù)以上列的代碼回答網(wǎng)友的提問: if (dev->bus->probe)這個表示是否注冊了device結構,如果注冊了并且給device結構掛接上了驅(qū)動和總線,那么調(diào)用掛接在device結構中的總線的probe函數(shù).這里的device結構從哪里冒出來的?它在 bus_for_each_dev函數(shù)中:
int bus_for_each_dev(struct bus_type *bus, struct device *start,
???????????????????? void *data, int (*fn)(struct device *, void *))
{
??????? struct klist_iter i;
??????? struct device *dev;
?????? int error = 0;
??????? if (!bus)
??????????????? return -EINVAL;
??????? klist_iter_init_node(&bus->p->klist_devices, &i,????????
???????????????????????????? (start ? &start->knode_bus : NULL));
??????? while ((dev = next_device(&i)) && !error)????????????????????????????????????? //查找每個掛接在sdio總線上的設備,看他們是否有注冊,并調(diào)用相應的probe函數(shù)也就是
?????????????? error = fn(dev, data);??????????????????????????????????????????????????????????????? __driver_attach函數(shù).實際上就是查找device結構.
??????? klist_iter_exit(&i);
??????? return error;
}
關于platform_device結構與device結構的關系,就不用我在解釋了吧!probe函數(shù)的調(diào)用,就取決于你在你注冊的device結構中掛接的總線的類型了.因為調(diào)用 dev->bus->probe(dev); 所以清查看一下你注冊是掛接的總線的probe函數(shù)的參數(shù)即可. 一般來說,參數(shù)會是兩個,因為一類總線上總是可以掛接多個設備,所以我們還需要一個device_id. 如果行到else部分: else if (drv->probe) .這里調(diào)用驅(qū)動的probe函數(shù),由于我們注冊的是sdio_driver結構.看一看sdio_driver結構的申明,在include/linux/mmc/sdio_func.h中:
struct sdio_driver {
??????? char *name;
??????? const struct sdio_device_id *id_table;
??????? int (*probe)(struct sdio_func *, const struct sdio_device_id *);???????????????????? //很顯然probe就是兩個參數(shù),而不是一個.
??????? void (*remove)(struct sdio_func *);
??????? struct device_driver drv;
};
?
至于為什么你的probe是一個參數(shù),我希望你能給出完整的注冊流程才能分析,但是我認為根據(jù)我剛才分析的流程應該可以自己找出相應的代碼了.
首先我要做的是總領一下kobject、kset、subsystem這三個結構之間的關系。同樣以下內(nèi)容摘自我的工作筆記,但順序顛倒了,這里先行介紹可能對我們之后的代碼介紹要有些許幫助。
??? 關于kobject結構,首先每個目錄代表一個kobject對象,每個文件代表kobject的屬性(這里我實在不敢肯定!)。kobject是組成設備模型的基本結構,它只是一個類似c++中基類的東西。
?? 我想給出這樣一個比喻(我不知道這個比喻是否恰當):對于每一個目錄,它們都有一些共同的特點,比如都有名字,都有父子目錄。而對于“方法”來說,它們都有用來實現(xiàn)引用計數(shù)的方法。我們將這些共同點封裝起來形成了基類。但是這個基類并不能真正的代表一個目錄,加上了其它的特征之后才能成為一個真正的目錄,所以我們需要將kobject這個結構嵌入所有的sysfs下的目錄中以獲得kobject提供的一些特征,在加上每個目錄自己所獨有特征從而形成一個sysfs中的目錄。這也就是我理解的kobject結構的意義。那么kobject結構是否真的就對應于一個目錄呢?回答是肯定的,因為我在代碼中找到了這樣的答案,每一次調(diào)用kobject_add函數(shù),都會在這個函數(shù)中調(diào)用create_dir來創(chuàng)建一個目錄。
那么kset又是什么呢?為什么需要kset?還有subsystem呢?kobject、kset、subsystem這三個結構到底在設備樹中各自承擔什么樣的角色?kset是是嵌入相同類型結構的kobject的集合,對于c++來說意味著將繼承自所有基類的子類放在一起,這有什么意義呢?這樣想是否就會覺得很合理了,kset是用來將所有有著共同特點的目錄聯(lián)系在一起的東西。可能你還是要在內(nèi)心不停的問,這太抽象了太抽象了,能舉個例子嗎?是的,比如我們的設備樹下的pci總線目錄/sys/bus/pci下掛接著很多的設備和驅(qū)動(當然有很多的設備和驅(qū)動可以掛著在pci總線下),那么我們?nèi)绾螌⑺鼈兟?lián)系在一起呢?是的,是通過kset結構,那么kset結構是個什么樣的地位呢?噢,我想之前我理解錯了,它不是/sys/bus/pci目錄,也不是/sys/bus/pci/drivers目錄中的某個具體驅(qū)動,它剛好就是/sys/bus/pci/drivers目錄。不相信? 那么打開你們的linux操作系統(tǒng),看一看是否每一條總線都有著drives和devices兩個目錄?它們兩個都是嵌入了kset結構;那么/sys/bus/pci/drivers目錄中的某個具體驅(qū)動呢?它就是嵌入了kobject結構的目錄;/sys/bus/pci目錄呢?猜對了,它就是subsystem結構的嵌入。 還有要補充的嗎?是呢,現(xiàn)在subsystem貌似被取消了,取代它的就是kset結構。這樣也就是說一系列的kset就組成了subsystem。
??? 我似乎忘記了自己問了三個問題,對的,我還需要問答自己看《linux設備驅(qū)動第三版》提出來的第三個問題:為什么需要kset?提出這個問題的想法很幼稚,難道我們不能夠只用kobject結構將掛接在一條總線上的驅(qū)動或者設備都鏈接在一起嗎?只要我們有l(wèi)ist_head!linux內(nèi)核的作者回答了我這個問題,你需要一個容器來管理它們,就像c++中的容器一樣。但這至少說明了kset并不是必要的存在。那么kset是如何扮演容器的角色的呢?這個問題,我們需要看后面的圖解來回答。
????以上是摘自我穿插在工作筆記中的關于kobject、ket、subsystem三個結構的描述,可能大家讀完這段解釋仍然無法形成具體的概念,仍然覺得很抽象。沒關系,因為我在代碼分析之后還會有總結性的舉例。那時還不明白我就沒轍了!!!
??? 好了,現(xiàn)在我們得費勁心思的捋一遍我們的驅(qū)動注冊代碼,以便找到設備樹添加的關鍵部分。我想我又得強調(diào)一下,我的介紹是SDIO驅(qū)動,所以請大家看著linux內(nèi)核代碼drivers/mmc中關于sdio的驅(qū)動來理解我下面的筆記中的內(nèi)容(想不看內(nèi)核代碼就理解設備樹,我想太難太難)
?? 有關sbi_register函數(shù)(這是在我的wlan驅(qū)動代碼中的函數(shù),并不需要你太多的關注)中sdio_register_driver函數(shù)(從現(xiàn)在開始就都是內(nèi)核函數(shù)了)注冊驅(qū)動的介紹,在sdio_register_driver中將會指明驅(qū)動的名稱(這里是”wlan_sdio”),此函數(shù)的參數(shù)為sdio_driver結構。驅(qū)動所掛接的總線sdio_bus_type,其結構類型為:
? static struct bus_type sdio_bus_type =?{
???????????? .name???????????? = "sdio",???????????????? //總線類型
???????????? .dev_attrs?????? = sdio_dev_attrs,????????????? //屬性
???????????? .match?????????? = sdio_bus_match,???????? //ops
??????????? .uevent?????????? = sdio_bus_uevent,
???????????? .probe??????????? = sdio_bus_probe,?
???????????? .remove????????? = sdio_bus_remove,
};
??? 這個結構將在sdio_register_driver函數(shù)中被賦值以產(chǎn)生device_driver結構。也就是說device_driver被包含在sdio_driver中。隨后調(diào)用函數(shù)driver_register,其參數(shù)為device_driver(此結構中定義了bus_type,也就是驅(qū)動掛接的總線類型)。至此將轉(zhuǎn)入所有驅(qū)動(不止是sdio卡驅(qū)動)的注冊代碼中。此時的驅(qū)動結構已經(jīng)變?yōu)閐evice_drive(內(nèi)核定義的驅(qū)動結構)。
??? driver_register將會完成掛接驅(qū)動至總線及生成設備樹的過程,其完成的任務大致包括:
??? 1、設置設備驅(qū)動中kobject的名字
????2、將kobject添加至sysfs中,也就是在sysfs樹中創(chuàng)建一個目錄(kobject中有一個函數(shù)create_dir用于創(chuàng)建目錄。)
????? 3、調(diào)用driver_attach函數(shù)完成probe?
????? 4、driver_create_file創(chuàng)建文件屬性,會生成一個屬性為driver_attr_uevent的屬性文件。
????? 5、add_bind_files生成屬性為driver_attr_unbind和driver_attr_bind的屬性文件,關于文件的屬性,它定義了文件的讀寫方式。
如此抽象的描述及流水賬般的記述我發(fā)現(xiàn)還是很沒有說服力,因此我決定給出一個具體的例子,并給出代碼的實現(xiàn)。先看例子——讓我們看一下/sys/bus/下的目錄,然后來個具體的描述:已知我們的總線的目錄名字為”sdio”。也就是說在/sys/bus目錄下有一個目錄叫sdio,即/sys/bus/sdio。它是怎么形成的?內(nèi)核中有”sdio”總線的驅(qū)動,找到這個函數(shù)bus_register(&sdio_bus_type);就是用來注冊總線的。我們在前面不也看到了driver_register函數(shù)嗎,是的,你猜對了,還有一個函數(shù)叫device_register。在這個函數(shù)調(diào)用完成后,就會得到/sys/bus/目錄下的sdio目錄了。那么驅(qū)動的名字”wlan_sdio”又是如何插入到/sys/bus/drivers/wlan_sdio目錄下的呢。在driver_register->driver_register->bus_add_driver函數(shù)中有個重要的語句drv->kobj.kset = &bus->drivers;想象一下我們折騰了那么長時間的kobject與kset的意義,是的,這里就是將driver的kobj所屬的kset掛接上總線的kset。我們這里顯得很繞對嗎?幸運的是我在網(wǎng)上找到了一個非常棒的示意圖:
?
? 我該怎么樣來形容這個圖呢? 它把依賴關系說的已經(jīng)足夠清楚了!!我這里唯一要解釋的僅僅是在驅(qū)動文件夾被正確后所謂的文件屬性有在那里。同樣我們順著目錄/sys/bus/sdio/driver/wlan_sdio/下我們發(fā)現(xiàn)了bind、unbind和new_id三個文件。
?? 好了,別忘了我們在前面提過的問題,kset是如何扮演容器的角色的呢?圖中很清楚吧,看看粉紅色的箭頭,kset children list用來將將同類型的kobject連接起來以達到容器的效果。
?但是我們的驅(qū)動還沒有結束,關于這個圖的建立,我們勢必要用代碼才能說得明白。
我們還是花費一點時間來看一下內(nèi)核中的代碼,關于sdio總線注冊的代碼部分,其它的部分,大家類舉就可以了。懂了這段自然就懂了全部:
int bus_register(struct bus_type * bus)
{
?????? int retval;
?
?????? BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);
????? retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);?? ?//總線的名字”sdio”,我們說過了一個kobject對應一個目錄,這時會為這個目錄賦值名字。
?????? if (retval)
????????????? goto out;
?????? bus->subsys.kobj.kset = &bus_subsys;????????????????????? ??//將其kset指向bus_subsys.,如何理解? 看看ldd_bus_type指向bus_subsys的那條藍線
?????? retval = subsystem_register(&bus->subsys);???????????? ?//bus->subsys的注冊,實際上是用kset指針將其鏈接在一起。好吧 我得承認實際上這里取消了subsysem結構的概念,用kset代替了。這里會創(chuàng)建一個目錄。它是一個kset也是一個kobject,因為kset包含了kobject。
?????? if (retval)
????????????? goto out;
?????? retval = bus_create_file(bus, &bus_attr_uevent);?????????? ?//創(chuàng)建屬性文件
?????? if (retval)
????????????? goto bus_uevent_fail;
?????? kobject_set_name(&bus->devices.kobj, "devices");??????? ?//設置devices kset的名字為devices
?????? bus->devices.kobj.parent = &bus->subsys.kobj;??????????? //參見ldd_bus_type->devices指向ldd_bus_type->sub_sys的紅色箭頭(注意這里也不是subsystem,而是kset)
?????? retval = kset_register(&bus->devices);???????????????? //創(chuàng)建devices命名的目錄
?????? if (retval)
????????????? goto bus_devices_fail;
?????? kobject_set_name(&bus->drivers.kobj, "drivers");?????? //設置devices kset的名字為drivers
?????? bus->drivers.kobj.parent = &bus->subsys.kobj;???????? //同樣參見ldd_bus_type->drivers指向ldd_bus_type->sub_sys的紅色箭頭(注意這里也不是subsystem,而是kset)。
????? bus->drivers.ktype = &driver_ktype;?????????? ????????//對kobject默認屬性的賦值
?????? retval = kset_register(&bus->drivers);??????????????? ?//創(chuàng)建drivers命名的目錄
?????? if (retval)
????????????? goto bus_drivers_fail;
????? klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);?? ?//klist結構的初始化,關于klist鏈接的作用我們已經(jīng)說得很清楚了
?????? klist_init(&bus->klist_drivers, NULL, NULL);
????? bus->drivers_autoprobe = 1;
?????? retval = add_probe_files(bus);???????????????????? ?//添加探測屬性
?????? if (retval)
????????????? goto bus_probe_files_fail;
????? retval = bus_add_attrs(bus);????????????????????? ?//添加其他屬性
?????? if (retval)
????????????? goto bus_attrs_fail;
?
?????? pr_debug("bus type '%s' registered\n", bus->name);
?????? return 0;
bus_attrs_fail:
?????? remove_probe_files(bus);
bus_probe_files_fail:
?????? kset_unregister(&bus->drivers);
bus_drivers_fail:
????? kset_unregister(&bus->devices);
bus_devices_fail:
?????? bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
?????? subsystem_unregister(&bus->subsys);
out:
?????? return retval;
}
至此 我們終于理順了整個過程。
?
總結
以上是生活随笔為你收集整理的linux设备驱动——andriod平台wlan驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在Fedora 14上安装Sun JDK
- 下一篇: Android/linux(earlys