嵌入式Linux設備驅(qū)動之總線、設備、驅(qū)動之間有何關系?
Linux設備驅(qū)動的難點在于復雜的,龐大的結構。理清楚結構和一個結構與另外結構的關系,以及l(fā)inux設備驅(qū)動的層次性和層次封裝抽象性。對于linux設備驅(qū)動的結構有點像C++中的類,而層次與抽象有點像繼承的關系。
一、總線、設備、驅(qū)動的主要三個結構關系
structbus_type
---------------------------------
struct bus_type中為devices和drivers準備了兩個鏈表:
struct klist klist_devices
struct klist klist_drivers
struct device
---------------------------------
struct device有兩個成員
struct bus_type *bus 記錄的是這個設備連在哪條總線上
struct device_driver *driver 記錄的是這個設備用的是哪個驅(qū)動
struct device_driver
---------------------------------
struct device_driver同樣有兩個成員
struct bus_type *bus 代表的是這個驅(qū)動屬于哪條總線
struct klist klist_devices 記錄的是這個驅(qū)動支持的那些設備,沒錯,是devices(復數(shù)),因為一個驅(qū)動程序可以支持一個或多個設備,反過來一個設備則只會綁定給一個驅(qū)動程序.
二、總線,設備,驅(qū)動的關聯(lián)
總線將設備和驅(qū)動綁定。在系統(tǒng)每注冊一個設備的時候,會尋找與之匹配的驅(qū)動;相反的,在系統(tǒng)每 注冊一個驅(qū)動的時候,會尋找與之匹配的設備,而匹配由總線完成。一個現(xiàn)實的Linux設備和驅(qū)動通常都需要掛接在一種總線上。設備與驅(qū)動的關聯(lián)通過總線的match()方法進行匹配,驅(qū)動掛載總線時與所有設備進行匹配,設備掛載總線時與所有的驅(qū)動進行匹配,所以驅(qū)動和設備的掛載無先后之分。匹配成功后會通過調(diào)用驅(qū)動的probo()方法來初始化設備。
三、總線,設備,驅(qū)動的注冊
設備與驅(qū)動需要掛載在總線上,需要指明驅(qū)動與設備是屬于哪條總線的,所以設備與驅(qū)動需要注冊。而總線在linux系統(tǒng)中也是屬于設備,所以總線也要注冊,同時要先有總線而后才能注冊設備和驅(qū)動,所以總線要先注冊。
總線在linux系統(tǒng)中有倆種,一是實際存在的總線 pci usb 等等,還有一類是虛擬存在的總線 platform ,platform總線主要是用于集成在SoC系統(tǒng)的設備,使得每一個設備都屬于一條總線,相應的設備稱為platform_device,而驅(qū)動成為 platform_driver。linux驅(qū)動中platform總線用的非常多,以platform總線說明總線,設備,驅(qū)動的注冊順序,注意這里是以先調(diào)加設備為例。
1. platform_bus_type -- 總線 先被kenrel 注冊。
2. 系統(tǒng)初始化過程中調(diào)用platform_add_devices 或者platform_device_register ,將平臺設備(platform devices) 注冊到平臺總線中( platform_bus_type )
3. 平臺驅(qū)動(platform driver) 與平臺設備(platform device) 的關聯(lián)是在platform_driver_register 或者driver_register 中實現(xiàn),一般這個函數(shù)在驅(qū)動的初始化過程調(diào)用。
通過這三步,就將平臺總線,設備,驅(qū)動關聯(lián)起來。
1. platform bus 先被kenrel 注冊。
------------------------------------------------------
do_basic_setup() --> - driver_init() --> - platform_bus_init() -->bus_register()
2. 系統(tǒng)初始化過程中調(diào)用platform_add_devices 或者platform_device_register ,將平臺設備(platform devices) 注冊到平臺總線中( platform_bus_type )
------------------------------------------------------
系統(tǒng)啟動階段,總線的驅(qū)動鏈表還是空的,所以啟動階段的platform_add_devices() 只負責將設備添加到總線的設備鏈表上。
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 設備和驅(qū)動匹配成功
0 設備已經(jīng)注冊,但是總線上沒有與之相匹配的驅(qū)動( 系統(tǒng)啟動階段,由于總線上還沒有驅(qū)動,所以設備在此匹配不到與之對應的驅(qū)動,只是將其添加到總線的設備鏈表)
-ENODEV 設備沒有注冊(registered) -- 設備在哪里注冊?
如果設備和驅(qū)動匹配成功; 或者設備已經(jīng)注冊,但是總線上沒有與之相匹配的驅(qū)動 ,bus_attach_device() 將調(diào)用klist_add_tail() 將設備添加到總線的設備鏈表尾部。
四、附錄linux內(nèi)核中的platform的幾個結構源代碼
[cpp] view plain copy
1. // 所在目錄:kernel/include/linux/platform_device.h
2. struct platform_device
3. {
4. const char * name;/* 設備名 */
5. u32 id;
6. struct device dev;
7. u32 num_resources;/* 設備所使用各類資源數(shù)量 */
8. struct resource * resource;/* 資源 */
9. };
10. // 所在目錄:include/linux/ioport.h
11. struct resource
12. {
13. resource_size_t start; /* 資源的開始值 */
14. resource_size_t end; /* 資源的結束值 */
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. };