在Linux2.6以后的設(shè)備驅(qū)動(dòng)模型中,需關(guān)心總線,設(shè)備和驅(qū)動(dòng)這三種實(shí)體,總線將設(shè)備和驅(qū)動(dòng)綁定。在系統(tǒng)每注冊(cè)一個(gè)設(shè)備的時(shí)候,會(huì)尋找與之匹配的驅(qū)動(dòng);相反,在系統(tǒng)每注冊(cè)一個(gè)驅(qū)動(dòng)的時(shí)候,會(huì)尋找與之匹配的設(shè)備,而匹配由總線完成。?
對(duì)于依附在USB、PCI、I2C、SPI等物理總線來 這些都不是問題。但是在嵌入式系統(tǒng)里面,在Soc系統(tǒng)中集成的獨(dú)立外設(shè)控制器,掛接在Soc內(nèi)存空間的外設(shè)等卻不依附在此類總線。基于這一背景,Linux發(fā)明了一種總線,稱為platform。?
相對(duì)于USB、PCI、I2C、SPI等物理總線來說,platform總線是一種虛擬、抽象出來的總線,實(shí)際中并不存在這樣的總線。?
platform總線相關(guān)代碼:driveraseplatform.c 文件?
相關(guān)結(jié)構(gòu)體定義:includelinuxplatform_device.h 文件中
platform總線管理下最重要的兩個(gè)結(jié)構(gòu)體是platform_device和platform_driver?
分別表示設(shè)備和驅(qū)動(dòng)?
在Linux中的定義如下?
一:platform_driver
//includelinuxplatform_device.h struct platform_driver { int (*probe)(struct platform_device *); //探測(cè)函數(shù),在注冊(cè)平臺(tái)設(shè)備時(shí)被調(diào)用 int (*remove)(struct platform_device *); //刪除函數(shù),在注銷平臺(tái)設(shè)備時(shí)被調(diào)用 void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); //掛起函數(shù),在關(guān)機(jī)被調(diào)用 int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *);//恢復(fù)函數(shù),在開機(jī)時(shí)被調(diào)用 struct device_driver driver;//設(shè)備驅(qū)動(dòng)結(jié)構(gòu)};
1
2
3
4
5
6
7
8
9
10
11
struct device_driver { const char * name; struct bus_type * bus; struct kobject kobj; struct klist klist_devices; 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);};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
二:platform_device
struct platform_device { const char * name;//設(shè)備名稱 u32 id;//取-1 struct device dev;//設(shè)備結(jié)構(gòu) u32 num_resources;// resource結(jié)構(gòu)個(gè)數(shù) struct resource * resource;//設(shè)備資源};
1
2
3
4
5
6
7
resource結(jié)構(gòu)體也是描述platform_device的一個(gè)重要結(jié)構(gòu)體 該元素存入了最為重要的設(shè)備資源信息
struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child;};
1
2
3
4
5
6
7
我們通常關(guān)心start,end,flags。它們分別標(biāo)明了資源的開始值,結(jié)束值和類型,flags可以為IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等,start,end的含義會(huì)隨著flags變更,如當(dāng)flags為IORESOURCE_MEM時(shí),start,end分別表示該platform_device占據(jù)的內(nèi)存的開始地址和結(jié)束地址;當(dāng)flags為,IORESOURCE_IRQ時(shí),start,end分別表示該platform_device使用的中斷號(hào)的開始值和結(jié)束值,如果只使用了1個(gè)中斷號(hào),開始和結(jié)束值相同。對(duì)于同種類型的資源而言,可以有多份,例如某設(shè)備占據(jù)了兩個(gè)內(nèi)存區(qū)域,則可以定義兩個(gè)IORESOURCE_MEM資源。? 對(duì)resource的定義也通常在BSP的板文件中運(yùn)行,而在具體的設(shè)備驅(qū)動(dòng)中通過platform_get_resource()這樣的API來獲取,此API的原型為:
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num)
1
2
例如在archarmmach-at91Board-sam9261ek.c板文件中為DM9000網(wǎng)卡定義了如下的resource
static struct resource at91sam9261_dm9000_resource[] = { [0] = { .start = AT91_CHIPSELECT_2, .end = AT91_CHIPSELECT_2 + 3, .flags = IORESOURCE_MEM }, [1] = { .start = AT91_CHIPSELECT_2 + 0x44, .end = AT91_CHIPSELECT_2 + 0xFF, .flags = IORESOURCE_MEM }, [2] = { .start = AT91_PIN_PC11, .end = AT91_PIN_PC11, .flags = IORESOURCE_IRQ }};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在DM9000網(wǎng)卡驅(qū)動(dòng)中則是通過如下辦法拿到這3份資源
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
1
2
3
對(duì)于irq而言platform_get_resource()還有一個(gè)進(jìn)行了封裝的變體platform_get_irq(),? api原型是
int platform_get_irq(struct platform_device *dev, unsigned int num){ struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num); return r ? r->start : -ENXIO;}
1
2
3
4
5
實(shí)際上這個(gè)函數(shù)也是調(diào)用platform_get_resource(dev, IORESOURCE_IRQ, num)來獲取資源
設(shè)備除了可以在BSP中定義資源以外,還可以附加一些數(shù)據(jù)信息,因?yàn)閷?duì)設(shè)備的硬件描述除了中斷,內(nèi)存等標(biāo)準(zhǔn)資源以外,可能還會(huì)有一些配置信息,這些配置信息也依賴于板,不適宜直接放置在設(shè)備驅(qū)動(dòng)上,因此,platform也提供了platform_data的支持,例如對(duì)于dm9000
static struct dm9000_plat_data dm9000_platdata = { .flags = DM9000_PLATF_16BITONLY,};static struct platform_device at91sam9261_dm9000_device = { .name = "dm9000", .id = 0, .num_resources = ARRAY_SIZE(at91sam9261_dm9000_resource), .resource = at91sam9261_dm9000_resource, .dev = { .platform_data = &dm9000_platdata, }};
1
2
3
4
5
6
7
8
9
10
11
12
13
獲取platform_data的API是dev_get_platdata()
struct dm9000_plat_data *pdata = dev_get_platdata(&pdev->dev);
1
三:platform總線? 系統(tǒng)為platform總線定義了一個(gè)bus_type的實(shí)例platform_bus_type,其定義位于drivers/base/platform.c下
struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops,};
1
2
3
4
5
6
7
最重要的是match函數(shù),真是此成員函數(shù)確定了platform_device和platform_driver之間如何匹配的
static int platform_match(struct device *dev, struct device_driver *drv){ struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可知有3中情況下platform_device和platform_driver匹配? 1.基于設(shè)備樹風(fēng)格的匹配? 2.匹配ID表(即platform_device設(shè)備名是否出現(xiàn)在platform_driver的id表內(nèi))? 3.匹配platform_device設(shè)備名和驅(qū)動(dòng)的名字? 在4.0還有一種情況是基于ACPI風(fēng)格的匹配
對(duì)于設(shè)備驅(qū)動(dòng)的開發(fā)? 其設(shè)計(jì)順序?yàn)槎x platform_device -> 注冊(cè) platform_device-> 定義 platform_driver-> 注冊(cè) platform_driver 。? 這里選用的例子是q40kbd,在/drivers/input/serio目錄里。這是一個(gè)鍵盤控制器驅(qū)動(dòng)? 這里直接分析初始化函數(shù)
static int __init q40kbd_init(void){ int error; if (!MACH_IS_Q40) return -EIO; /* platform總線驅(qū)動(dòng)的注冊(cè) */ error = platform_driver_register(&q40kbd_driver); if (error) return error; /* 分配一個(gè)platform設(shè)備 */ q40kbd_device = platform_device_alloc("q40kbd", -1); if (!q40kbd_device) goto err_unregister_driver; /* platform設(shè)備注冊(cè) */ error = platform_device_add(q40kbd_device); if (error) goto err_free_device; return 0; err_free_device: platform_device_put(q40kbd_device); err_unregister_driver: platform_driver_unregister(&q40kbd_driver); return error;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
驅(qū)動(dòng)注冊(cè)函數(shù)是 platform_driver_register()函數(shù)
int platform_driver_register(struct platform_driver *drv){ //把驅(qū)動(dòng)的總線設(shè)置為platform總線 drv->driver.bus = &platform_bus_type; //依次設(shè)置驅(qū)動(dòng)的各個(gè)函數(shù)指針 if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; if (drv->suspend) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; //調(diào)用driver_register注冊(cè)驅(qū)動(dòng) return driver_register(&drv->driver);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 初始化后調(diào)用bus_add_driver函數(shù)執(zhí)行注冊(cè)過程 */int driver_register(struct device_driver * drv){ 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 ", drv->name); } klist_init(&drv->klist_devices, NULL, NULL); return bus_add_driver(drv);}
1
2
3
4
5
6
7
8
9
10
11
12
為總線增加一個(gè)驅(qū)動(dòng)
int bus_add_driver(struct device_driver *drv){ struct bus_type * bus = get_bus(drv->bus); int error = 0; if (!bus) return -EINVAL; pr_debug("bus %s: add driver %s ", bus->name, drv->name); /* 為kobject結(jié)構(gòu)設(shè)置名字 */ error = kobject_set_name(&drv->kobj, "%s", drv->name); if (error) goto out_put_bus; drv->kobj.kset = &bus->drivers; /* 為sysfs文件系統(tǒng)創(chuàng)建設(shè)備的相關(guān)文件 */ if ((error = kobject_register(&drv->kobj))) goto out_put_bus; if (drv->bus->drivers_autoprobe) { /* 驅(qū)動(dòng)加入總線的驅(qū)動(dòng)列表 */ error = driver_attach(drv); if (error) goto out_unregister; } /* 在sysfs創(chuàng)建驅(qū)動(dòng)的屬性文件和模塊 */ klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed ", __FUNCTION__, drv->name); } error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed ", __FUNCTION__, drv->name); } return error;out_unregister: kobject_unregister(&drv->kobj);out_put_bus: put_bus(bus); return error;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
執(zhí)行驅(qū)動(dòng)加載
int driver_attach(struct device_driver * drv){ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);}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; /* 初始化一個(gè)klist_iter結(jié)構(gòu) 目的是在雙向鏈表中定位一個(gè)成員 */ klist_iter_init_node(&bus->klist_devices, &i, (start ? &start->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) //對(duì)每個(gè)設(shè)備都調(diào)用fn函數(shù)指針 fn就是傳入的函數(shù)指針__driver_attach error = fn(dev, data); klist_iter_exit(&i); return error;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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); /* 獲取設(shè)備的鎖 */ down(&dev->sem); if (!dev->driver) driver_probe_device(drv, dev); up(&dev->sem); if (dev->parent) up(&dev->parent->sem); return 0;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int driver_probe_device(struct device_driver * drv, struct device * dev){ int ret = 0; if (!device_is_registered(dev)) return -ENODEV; /* 調(diào)用總線配置的match函數(shù) */ if (drv->bus->match && !drv->bus->match(dev, drv)) goto done; pr_debug("%s: Matched Device %s with Driver %s ", drv->bus->name, dev->bus_id, drv->name); /* 調(diào)用really_probe函數(shù) */ ret = really_probe(dev, drv);done: return ret;}static int really_probe(struct device *dev, struct device_driver *drv){ int ret = 0; atomic_inc(&probe_count); pr_debug("%s: Probing driver %s with device %s ", drv->bus->name, drv->name, dev->bus_id); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed ", __FUNCTION__, dev->bus_id); goto probe_failed; } /* 調(diào)用總線的probe函數(shù) */ if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; /* 如果驅(qū)動(dòng)提供了Probe函數(shù),則調(diào)用驅(qū)動(dòng)的probe函數(shù) */ } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } /* 設(shè)備發(fā)現(xiàn)了驅(qū)動(dòng) 通過sysfs創(chuàng)建一些文件 和設(shè)備做符號(hào)鏈接 */ driver_bound(dev); ret = 1; pr_debug("%s: Bound Device %s to Driver %s ", drv->bus->name, dev->bus_id, drv->name); goto done;probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d ", drv->name, dev->bus_id, ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0;done: atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
driver_probe_device() 這個(gè)函數(shù)實(shí)際上做了兩件事? 1.調(diào)用總線提供的match函數(shù)。如果檢查通過 說明設(shè)備和驅(qū)動(dòng)是匹配的 設(shè)備所指的驅(qū)動(dòng)指針要賦值為當(dāng)前驅(qū)動(dòng)? 2.探測(cè)(probe) 首先調(diào)用總線提供的probe函數(shù),如果驅(qū)動(dòng)有自己的Probe函數(shù),還要調(diào)用驅(qū)動(dòng)的probe函數(shù)
platform總線的match函數(shù)在上文有介紹 三種情況均可匹配? platform總線的probe函數(shù)就是platform_drv_probe() 這個(gè)函數(shù)是個(gè)封裝函數(shù) 它只是簡(jiǎn)單的調(diào)用了驅(qū)動(dòng)的Probe函數(shù) 驅(qū)動(dòng)的Probe函數(shù)就是q40kbd_probe
static int __devinit q40kbd_probe(struct platform_device *dev){ q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!q40kbd_port) return -ENOMEM; q40kbd_port->id.type = SERIO_8042; q40kbd_port->open = q40kbd_open; q40kbd_port->close = q40kbd_close; q40kbd_port->dev.parent = &dev->dev; strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name)); strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys)); //注冊(cè)serio結(jié)構(gòu)變量 serio_register_port(q40kbd_port); printk(KERN_INFO "serio: Q40 kbd registered "); return 0;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
綜上所述,platform總線驅(qū)動(dòng)注冊(cè)就是遍歷各個(gè)設(shè)備 檢查是否和驅(qū)動(dòng)匹配
接下來分析platform設(shè)備的注冊(cè)
int platform_device_add(struct platform_device *pdev){ int i, ret = 0; if (!pdev) return -EINVAL; /* 設(shè)置設(shè)備的父類型 */ if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; /* 設(shè)置設(shè)備的總線類型為platform_bus_type */ pdev->dev.bus = &platform_bus_type; if (pdev->id != -1) snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id); else strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); /* 把設(shè)備IO端口和IO內(nèi)存資源注冊(cè)到系統(tǒng) */ for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) r->name = pdev->dev.bus_id; p = r->parent; if (!p) { if (r->flags & IORESOURCE_MEM) p = &iomem_resource; else if (r->flags & IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d ", pdev->dev.bus_id, i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s ", pdev->dev.bus_id, pdev->dev.parent->bus_id); ret = device_add(&pdev->dev); if (ret == 0) return ret; failed: while (--i >= 0) if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(&pdev->resource[i]); return ret;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int device_add(struct device *dev){ struct device *parent = NULL; char *class_name = NULL; struct class_interface *class_intf; int error = -EINVAL; dev = get_device(dev); if (!dev || !strlen(dev->bus_id)) goto Error; pr_debug("DEV: registering device: ID = '%s' ", dev->bus_id); /* 獲得父設(shè)備 */ parent = get_device(dev->parent); error = setup_parent(dev, parent); if (error) goto Error; /* first, register with generic layer. */ /* 這是kobject的名字 */ kobject_set_name(&dev->kobj, "%s", dev->bus_id); /* 在sys目錄生成設(shè)備目錄 */ error = kobject_add(&dev->kobj); if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); /* notify clients of device entry (new way) */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); /* 設(shè)置uevent屬性 */ dev->uevent_attr.attr.name = "uevent"; dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR; if (dev->driver) dev->uevent_attr.attr.owner = dev->driver->owner; dev->uevent_attr.store = store_uevent; dev->uevent_attr.show = show_uevent; error = device_create_file(dev, &dev->uevent_attr); if (error) goto attrError; /* 設(shè)備的屬性文件 */ if (MAJOR(dev->devt)) { struct device_attribute *attr; attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) { error = -ENOMEM; goto ueventattrError; } attr->attr.name = "dev"; attr->attr.mode = S_IRUGO; if (dev->driver) attr->attr.owner = dev->driver->owner; attr->show = show_dev; error = device_create_file(dev, attr); if (error) { kfree(attr); goto ueventattrError; } dev->devt_attr = attr; } /* 創(chuàng)建設(shè)備類的符號(hào)鏈接 */ if (dev->class) { sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, "subsystem"); /* If this is not a "fake" compatible device, then create the * symlink from the class to the device. */ if (dev->kobj.parent != &dev->class->subsys.kobj) sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, dev->bus_id); if (parent) { sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");#ifdef CONFIG_SYSFS_DEPRECATED class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);#endif } } /* 設(shè)備的能源管理 */ if ((error = device_add_attrs(dev))) goto AttrsError; if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) goto BusError; kobject_uevent(&dev->kobj, KOBJ_ADD); bus_attach_device(dev); /* 設(shè)備加入父設(shè)備的鏈表 */ if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { down(&dev->class->sem); /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); up(&dev->class->sem); } Done: kfree(class_name); put_device(dev); return error; BusError: device_pm_remove(dev); PMError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: if (dev->devt_attr) { device_remove_file(dev, dev->devt_attr); kfree(dev->devt_attr); } if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); /* If this is not a "fake" compatible device, remove the * symlink from the class to the device. */ if (dev->kobj.parent != &dev->class->subsys.kobj) sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); if (parent) {#ifdef CONFIG_SYSFS_DEPRECATED char *class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_remove_link(&dev->parent->kobj, class_name); kfree(class_name);#endif sysfs_remove_link(&dev->kobj, "device"); } } ueventattrError: device_remove_file(dev, &dev->uevent_attr); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: if (parent) put_device(parent); goto Done;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
遍歷驅(qū)動(dòng) 為設(shè)備找到合適的驅(qū)動(dòng)
int device_attach(struct device * dev){ int ret = 0; down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; else { dev->driver = NULL; ret = 0; } } else { ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); } up(&dev->sem); return ret;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
但是在Linux加入了設(shè)備樹的時(shí)候 就不再需要大量的板級(jí)信息了 譬如/arch/arm/plat-xxx和arch/arm/mach-xxx中platform的修改部分? 原先的
static struct resource xxx_resource[] = { [0] = { .start = ..., .end = ..., .flags = ..., }, [1] = { .start = ..., .end = ..., .flags = ..., }, [2] = { .start =..., .end = ..., .flags = ..., }};static struct platform_device xxx_device = { .name = "xxx", .id = -1, .num_resources = ARRAY_SIZE(xxx_resource), .resource = xxx_resource, .dev = { .platform_data = &xxx_platdata, }};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
之類的注冊(cè)platform_device,綁定resource,即內(nèi)存,irq等板級(jí)信息 現(xiàn)在都不再需要 其中platform_device會(huì)由內(nèi)核自動(dòng)展開。而這些resource實(shí)際來源于.dts中設(shè)備節(jié)點(diǎn)的reg,interrups屬性
再者就是platform_data這些平臺(tái)數(shù)據(jù)屬性化? 比如linux-3.4.2archarmmach-at91Board-sam9263ek.c下用如下代碼注冊(cè)gpio_keys設(shè)備 通過gpio_keys_platform_data來定義platform_data
static struct gpio_keys_button ek_buttons[] = { { /* BP1, "leftclic" */ .code = BTN_LEFT, .gpio = AT91_PIN_PC5, .active_low = 1, .desc = "left_click", .wakeup = 1, }, { /* BP2, "rightclic" */ .code = BTN_RIGHT, .gpio = AT91_PIN_PC4, .active_low = 1, .desc = "right_click", .wakeup = 1, }};static struct gpio_keys_platform_data ek_button_data = { .buttons = ek_buttons, .nbuttons = ARRAY_SIZE(ek_buttons),};static struct platform_device ek_button_device = { .name = "gpio-keys", .id = -1, .num_resources = 0, .dev = { .platform_data = &ek_button_data, }};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
設(shè)備驅(qū)動(dòng)drivers/input/keyboard/gpio_keys.c通過以下方法取得這個(gè)platform_data
struct device *dev = &pdev->dev;const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
1
2
轉(zhuǎn)移到設(shè)備樹后 platform_data就不再arch/arm/mach-xxx中 它需要從設(shè)備樹中獲取,比如一個(gè)電路板上有g(shù)pio_keys 則只需要在設(shè)備樹中添加類似arch/arm/boot/dts/exyons4210-origen.dts的代碼
gpio_keys { compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; up { label = "Up"; gpios = <&gpx2 0 0 0 2>; linux,code = <103>; }; down { label = "Down"; gpios = <&gpx2 1 0 0 2>; linux,code = <108>; };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
而在驅(qū)動(dòng)中通過of_開頭的讀屬性的API來讀取這些信息 并組織處gpio_keys_platform_data結(jié)構(gòu)體
gpio_keys_get_devtree_pdata(struct device *dev){ struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; struct fwnode_handle *child; int nbuttons; nbuttons = device_get_child_node_count(dev); if (nbuttons == 0) return ERR_PTR(-ENODEV); pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); button = (struct gpio_keys_button *)(pdata + 1); pdata->buttons = button; pdata->nbuttons = nbuttons; pdata->rep = device_property_read_bool(dev, "autorepeat"); device_property_read_string(dev, "label", &pdata->name); device_for_each_child_node(dev, child) { if (is_of_node(child)) button->irq = irq_of_parse_and_map(to_of_node(child), 0); if (fwnode_property_read_u32(child, "linux,code", &button->code)) { dev_err(dev, "Button without keycode
"); fwnode_handle_put(child); return ERR_PTR(-EINVAL); } fwnode_property_read_string(child, "label", &button->desc); if (fwnode_property_read_u32(child, "linux,input-type", &button->type)) button->type = EV_KEY; button->wakeup = fwnode_property_read_bool(child, "wakeup-source") || /* legacy name */ fwnode_property_read_bool(child, "gpio-key,wakeup"); button->can_disable = fwnode_property_read_bool(child, "linux,can-disable"); if (fwnode_property_read_u32(child, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; button++; } return pdata;}
?
評(píng)論
查看更多