嵌入式Linux設(shè)備驅(qū)動(dòng)之總線、設(shè)備、驅(qū)動(dòng)之間有何關(guān)系?
掃描二維碼
隨時(shí)隨地手機(jī)看文章
Linux設(shè)備驅(qū)動(dòng)的難點(diǎn)在于復(fù)雜的,龐大的結(jié)構(gòu)。理清楚結(jié)構(gòu)和一個(gè)結(jié)構(gòu)與另外結(jié)構(gòu)的關(guān)系,以及l(fā)inux設(shè)備驅(qū)動(dòng)的層次性和層次封裝抽象性。對(duì)于linux設(shè)備驅(qū)動(dòng)的結(jié)構(gòu)有點(diǎn)像C++中的類,而層次與抽象有點(diǎn)像繼承的關(guān)系。
一、總線、設(shè)備、驅(qū)動(dòng)的主要三個(gè)結(jié)構(gòu)關(guān)系
structbus_type
---------------------------------
struct bus_type中為devices和drivers準(zhǔn)備了兩個(gè)鏈表:
struct klist klist_devices
struct klist klist_drivers
struct device
---------------------------------
struct device有兩個(gè)成員
struct bus_type *bus 記錄的是這個(gè)設(shè)備連在哪條總線上
struct device_driver *driver 記錄的是這個(gè)設(shè)備用的是哪個(gè)驅(qū)動(dòng)
struct device_driver
---------------------------------
struct device_driver同樣有兩個(gè)成員
struct bus_type *bus 代表的是這個(gè)驅(qū)動(dòng)屬于哪條總線
struct klist klist_devices 記錄的是這個(gè)驅(qū)動(dòng)支持的那些設(shè)備,沒(méi)錯(cuò),是devices(復(fù)數(shù)),因?yàn)橐粋€(gè)驅(qū)動(dòng)程序可以支持一個(gè)或多個(gè)設(shè)備,反過(guò)來(lái)一個(gè)設(shè)備則只會(huì)綁定給一個(gè)驅(qū)動(dòng)程序.
二、總線,設(shè)備,驅(qū)動(dòng)的關(guān)聯(lián)
總線將設(shè)備和驅(qū)動(dòng)綁定。在系統(tǒng)每注冊(cè)一個(gè)設(shè)備的時(shí)候,會(huì)尋找與之匹配的驅(qū)動(dòng);相反的,在系統(tǒng)每 注冊(cè)一個(gè)驅(qū)動(dòng)的時(shí)候,會(huì)尋找與之匹配的設(shè)備,而匹配由總線完成。一個(gè)現(xiàn)實(shí)的Linux設(shè)備和驅(qū)動(dòng)通常都需要掛接在一種總線上。設(shè)備與驅(qū)動(dòng)的關(guān)聯(lián)通過(guò)總線的match()方法進(jìn)行匹配,驅(qū)動(dòng)掛載總線時(shí)與所有設(shè)備進(jìn)行匹配,設(shè)備掛載總線時(shí)與所有的驅(qū)動(dòng)進(jìn)行匹配,所以驅(qū)動(dòng)和設(shè)備的掛載無(wú)先后之分。匹配成功后會(huì)通過(guò)調(diào)用驅(qū)動(dòng)的probo()方法來(lái)初始化設(shè)備。
三、總線,設(shè)備,驅(qū)動(dòng)的注冊(cè)
設(shè)備與驅(qū)動(dòng)需要掛載在總線上,需要指明驅(qū)動(dòng)與設(shè)備是屬于哪條總線的,所以設(shè)備與驅(qū)動(dòng)需要注冊(cè)。而總線在linux系統(tǒng)中也是屬于設(shè)備,所以總線也要注冊(cè),同時(shí)要先有總線而后才能注冊(cè)設(shè)備和驅(qū)動(dòng),所以總線要先注冊(cè)。
總線在linux系統(tǒng)中有倆種,一是實(shí)際存在的總線 pci usb 等等,還有一類是虛擬存在的總線 platform ,platform總線主要是用于集成在SoC系統(tǒng)的設(shè)備,使得每一個(gè)設(shè)備都屬于一條總線,相應(yīng)的設(shè)備稱為platform_device,而驅(qū)動(dòng)成為 platform_driver。linux驅(qū)動(dòng)中platform總線用的非常多,以platform總線說(shuō)明總線,設(shè)備,驅(qū)動(dòng)的注冊(cè)順序,注意這里是以先調(diào)加設(shè)備為例。
1. platform_bus_type -- 總線 先被kenrel 注冊(cè)。
2. 系統(tǒng)初始化過(guò)程中調(diào)用platform_add_devices 或者platform_device_register ,將平臺(tái)設(shè)備(platform devices) 注冊(cè)到平臺(tái)總線中( platform_bus_type )
3. 平臺(tái)驅(qū)動(dòng)(platform driver) 與平臺(tái)設(shè)備(platform device) 的關(guān)聯(lián)是在platform_driver_register 或者driver_register 中實(shí)現(xiàn),一般這個(gè)函數(shù)在驅(qū)動(dòng)的初始化過(guò)程調(diào)用。
通過(guò)這三步,就將平臺(tái)總線,設(shè)備,驅(qū)動(dòng)關(guān)聯(lián)起來(lái)。
1. platform bus 先被kenrel 注冊(cè)。
------------------------------------------------------
do_basic_setup() --> - driver_init() --> - platform_bus_init() -->bus_register()
2. 系統(tǒng)初始化過(guò)程中調(diào)用platform_add_devices 或者platform_device_register ,將平臺(tái)設(shè)備(platform devices) 注冊(cè)到平臺(tái)總線中( platform_bus_type )
------------------------------------------------------
系統(tǒng)啟動(dòng)階段,總線的驅(qū)動(dòng)鏈表還是空的,所以啟動(dòng)階段的platform_add_devices() 只負(fù)責(zé)將設(shè)備添加到總線的設(shè)備鏈表上。
linux 2.6.26/drivers/base/platform.c
int platform_add_devices(struct platform_device **devs, int num)
{
...
ret = platform_device_register (devs[i]);
...
}
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev >dev);
return platform_device_add (pdev);
}
int platform_device_add (struct platform_device *pdev)
{
...
pdev >dev.bus = &platform_bus_type;
...
ret = device_add (&pdev >dev);
...
}
device_add() > bus_attach_device()
void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev >bus;
int ret = 0;
if (bus) {
if (bus >p >drivers_autoprobe)
ret = device_attach (dev);
WARN_ON(ret < 0);
if (ret >= 0)
klist_add_tail (&dev >knode_bus, &bus >p >klist_devices);
}
}
device_attach() 的返回值:
1 設(shè)備和驅(qū)動(dòng)匹配成功
0 設(shè)備已經(jīng)注冊(cè),但是總線上沒(méi)有與之相匹配的驅(qū)動(dòng)( 系統(tǒng)啟動(dòng)階段,由于總線上還沒(méi)有驅(qū)動(dòng),所以設(shè)備在此匹配不到與之對(duì)應(yīng)的驅(qū)動(dòng),只是將其添加到總線的設(shè)備鏈表)
-ENODEV 設(shè)備沒(méi)有注冊(cè)(registered) -- 設(shè)備在哪里注冊(cè)?
如果設(shè)備和驅(qū)動(dòng)匹配成功; 或者設(shè)備已經(jīng)注冊(cè),但是總線上沒(méi)有與之相匹配的驅(qū)動(dòng) ,bus_attach_device() 將調(diào)用klist_add_tail() 將設(shè)備添加到總線的設(shè)備鏈表尾部。
四、附錄linux內(nèi)核中的platform的幾個(gè)結(jié)構(gòu)源代碼
[cpp] view plain copy
1. // 所在目錄:kernel/include/linux/platform_device.h
2. struct platform_device
3. {
4. const char * name;/* 設(shè)備名 */
5. u32 id;
6. struct device dev;
7. u32 num_resources;/* 設(shè)備所使用各類資源數(shù)量 */
8. struct resource * resource;/* 資源 */
9. };
10. // 所在目錄:include/linux/ioport.h
11. struct resource
12. {
13. resource_size_t start; /* 資源的開(kāi)始值 */
14. resource_size_t end; /* 資源的結(jié)束值 */
15. const char *name; /* 資源的名字 */
16. unsigned long flags; /* 資源的類型值,如可以是:mem,io,irq,dma等等 */
17. struct resource *parent, *sibling, *child;
18. };
19. // 所在目錄:kernel/include/linux/ioport.h
20. struct platform_driver
21. {
22. int (*probe)(struct platform_device *);
23. int (*remove)(struct platform_device *);
24. void (*shutdown)(struct platform_device *);
25. int (*suspend)(struct platform_device *, pm_message_t state);
26. int (*suspend_late)(struct platform_device *, pm_message_t state);
27. int (*resume_early)(struct platform_device *);
28. int (*resume)(struct platform_device *);
29. struct pm_ext_ops *pm;
30. struct device_driver driver;
31. };
32. // 所在目錄:include/linux/device.h
33. struct device_driver
34. {
35. char * name;
36. struct bus_type * bus;
37. rwlock_t lock;
38. atomic_t refcount;
39. list_t bus_list;
40. list_t devices;
41. struct driver_dir_entry dir;
42. int (*probe) (struct device * dev);
43. int (*remove) (struct device * dev);
44. int (*suspend) (struct device * dev, u32 state, u32 level);
45. int (*resume) (struct device * dev, u32 level);
46. void (*release) (struct device_driver * drv);
47. };