platform匯流排和普通匯流排,驅動模型間的一些關係和理解
【摘要】本文以Linux 2.6.25 核心為例,分析了基於platform匯流排的驅動模型。首先介紹了Platform匯流排的基本概念,接著介紹了platform device和platform driver的定義和載入過程,分析了其與基類device 和driver的派生關係及在此過程中面向物件的設計思想。最後以ARM S3C2440中I2C控制器為例介紹了基於platform匯流排的驅動開發流程。
【關鍵字】platform_bus, platform_device, resource , platform_driver, file_operations
目錄
1 何謂platform bus? 2
2 device和platform_device 3
3 device_register和platform_device_register 5
4 device_driver和platform driver 8
5 driver_register 和platform_driver_register 10
6 bus、device及driver三者之間的關係 17
7 哪些適用於plarform驅動? 18
8 基於platform匯流排的驅動開發流程 18
8.1 初始化platform_bus 19
8.2 定義platform_device 22
8.3 註冊platform_device 22
8.4 定義platform_driver 28
8.5 註冊platform_driver 29
8.6 操作裝置 32
1 何謂platform bus?
Linux系統中許多部分對裝置是如何連結的並不感興趣,但是他們需要知道哪些型別的裝置是可以使用的。裝置模型提供了一種機制來對裝置進行分類,在更高的功能層面上描述這些裝置,並使得這些裝置對使用者空間可見。因此從2.6核心開始引入了裝置模型。
匯流排是處理器和一個或多個裝置之間的通道,在裝置模型中, 所有的裝置都通過匯流排相連。匯流排可以相互插入。裝置模型展示了匯流排和它們所控制的裝置之間的實際連線。
Platform匯流排是2.6 kernel中最近引入的一種虛擬匯流排,主要用來管理CPU的片上資源,具有更好的移植性,因此在2.6 kernel中,很多驅動都用platform改寫了。
platform_bus_type的定義如下:
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L609
-
609struct bus_type platform_bus_type = {
-
610 .name = "platform",
-
611 .dev_attrs = platform_dev_attrs,
-
612 .match = platform_match,
-
613 .uevent = platform_uevent,
-
614 .suspend = platform_suspend,
-
615 .suspend_late = platform_suspend_late,
-
616 .resume_early = platform_resume_early,
-
617 .resume = platform_resume,
-
618};
-
619EXPORT_SYMBOL_GPL(platform_bus_type);
-
http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L55
-
55struct bus_type {
-
56 const char *name;
-
57 struct bus_attribute *bus_attrs;
-
58 struct device_attribute *dev_attrs;
-
59 struct driver_attribute *drv_attrs;
-
60
-
61 int (*match)(struct device *dev, struct device_driver *drv);
-
62 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
-
63 int (*probe)(struct device *dev);
-
64 int (*remove)(struct device *dev);
-
65 void (*shutdown)(struct device *dev);
-
66
-
67 int (*suspend)(struct device *dev, pm_message_t state);
-
68 int (*suspend_late)(struct device *dev, pm_message_t state);
-
69 int (*resume_early)(struct device *dev);
-
70 int (*resume)(struct device *dev);
-
71
-
72 struct bus_type_private *p;
-
73};
匯流排名稱是"platform",其只是bus_type的一種,定義了匯流排的屬性,同時platform_bus_type還有相關操作方法,如掛起、中止、匹配及hotplug事件等。
匯流排bus是聯絡driver和device的中間樞紐。Device通過所屬的bus找到driver,由match操作方法進行匹配。
Bus、driver及devices的連線關係
2 device和platform_device
Plarform device會有一個名字用於driver binding(在註冊driver的時候會查詢driver的目標裝置的bus位置,這個過程稱為driver binding),另外IRQ以及地址空間等資源也要給出 。
platform_device結構體用來描述裝置的名稱、資源資訊等。該結構被定義在http://lxr.linux.no/#linux+v2.6.25/include/linux/platform_device.h#L16中,定義原型如下:
-
16struct platform_device {
-
17 const char * name; //定義平臺裝置的名稱,此處裝置的命名應和相應驅動程式命名一致
-
18 int id;
-
19 struct device dev;
-
20 u32 num_resources;
-
21 struct resource * resource; //定義平臺裝置的資源
-
22};
在這個結構裡封裝了struct device及struct resource。可知:platform_device由device派生而來,是一種特殊的device。
下面來看一下platform_device結構體中最重要的一個成員struct resource * resource。struct resource被定義在
-
http://lxr.linux.no/#linux+v2.6.25/include/linux/ioport.h#L18中,定義原型如下:
-
14/*
-
15 * Resources are tree-like, allowing
-
16 * nesting etc..
-
17 */
-
18struct resource {
-
19 resource_size_t start; //定義資源的起始地址
-
20 resource_size_t end; //定義資源的結束地址
-
21 const char *name; //定義資源的名稱
-
22 unsigned long flags; 定義資源的型別,比如MEM,IO,IRQ,DMA型別
-
23 struct resource *parent, *sibling, *child;
-
24};
這個結構表示裝置所擁有的資源,即I/O埠、I/O對映記憶體、中斷及DMA等。這裡的地址指的是實體地址。
另外還需要注意platform_device中的device結構,它詳細描述了裝置的情況,其為所有裝置的基類,定義如下:
-
http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L422
-
422struct device {
-
423 struct klist klist_children;
-
424 struct klist_node knode_parent; /* node in sibling list */
-
425 struct klist_node knode_driver;
-
426 struct klist_node knode_bus;
-
427 struct device *parent;
-
428
-
429 struct kobject kobj;
-
430 char bus_id[BUS_ID_SIZE]; /* position on parent bus */
-
431 struct device_type *type;
-
432 unsigned is_registered:1;
-
433 unsigned uevent_suppress:1;
-
434
-
435 struct semaphore sem; /* semaphore to synchronize calls to
-
436 * its driver.
-
437 */
-
438
-
439 struct bus_type *bus; /* type of bus device is on */
-
440 struct device_driver *driver; /* which driver has allocated this
-
441 device */
-
442 void *driver_data; /* data private to the driver */
-
443 void *platform_data; /* Platform specific data, device
-
444 core doesn't touch it */
-
445 struct dev_pm_info power;
-
446
-
447#ifdef CONFIG_NUMA
-
448 int numa_node; /* NUMA node this device is close to */
-
449#endif
-
450 u64 *dma_mask; /* dma mask (if dma'able device) */
-
451 u64 coherent_dma_mask;/* Like dma_mask, but for
-
452 alloc_coherent mappings as
-
453 not all hardware supports
-
454 64 bit addresses for consistent
-
455 allocations such descriptors. */
-
456
-
457 struct device_dma_parameters *dma_parms;
-
458
-
459 struct list_head dma_pools; /* dma pools (if dma'ble) */
-
460
-
461 struct dma_coherent_mem *dma_mem; /* internal for coherent mem
-
462 override */
-
463 /* arch specific additions */
-
464 struct dev_archdata archdata;
-
465
-
466 spinlock_t devres_lock;
-
467 struct list_head devres_head;
-
468
-
469 /* class_device migration path */
-
470 struct list_head node;
-
471 struct class *class;
-
472 dev_t devt; /* dev_t, creates the sysfs "dev" */
-
473 struct attribute_group **groups; /* optional groups */
-
474
-
475 void (*release)(struct device *dev);
-
476};
-
477
3 device_register和platform_device_register
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/core.c#L881
-
870/**
-
871 * device_register - register a device with the system.
-
872 * @dev: pointer to the device structure
-
873 *
-
874 * This happens in two clean steps - initialize the device
-
875 * and add it to the system. The two steps can be called
-
876 * separately, but this is the easiest and most common.
-
877 * I.e. you should only call the two helpers separately if
-
878 * have a clearly defined need to use and refcount the device
-
879 * before it is added to the hierarchy.
-
880 */
-
881int device_register(struct device *dev)
-
882{
-
883 device_initialize(dev);
-
884 return device_add(dev);
-
885}
-
初始化一個裝置,然後加入到系統中。
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L325
-
316/**
-
317 * platform_device_register - add a platform-level device
-
318 * @pdev: platform device we're adding
-
319 */
-
320int platform_device_register(struct platform_device *pdev)
-
321{
-
322 device_initialize(&pdev->dev);
-
323 return platform_device_add(pdev);
-
324}
-
325EXPORT_SYMBOL_GPL(platform_device_register);
我們看到註冊一個platform device分為了兩部分,初始化這個platform_device,然後將此platform_device新增到platform匯流排中。輸入引數platform_device可以是靜態的全域性裝置。
另外一種機制就是動態申請platform_device_alloc一個platform_device裝置,然後通過platform_device_add_resources及platform_device_add_data等新增相關資源和屬性。
無論哪一種platform_device,最終都將通過platform_device_add這冊到platform總線上。
-
229/**
-
230 * platform_device_add - add a platform device to device hierarchy
-
231 * @pdev: platform device we're adding
-
232 *
-
233 * This is part 2 of platform_device_register(), though may be called
-
234 * separately _iff_ pdev was allocated by platform_device_alloc().
-
235 */
-
236int platform_device_add(struct platform_device *pdev)
-
237{
-
238 int i, ret = 0;
-
239
-
240 if (!pdev)
-
241 return -EINVAL;
-
242
-
初始化裝置的parent為platform_bus,初始化驅備的匯流排為platform_bus_type。
-
243 if (!pdev->dev.parent)
-
244 pdev->dev.parent = &platform_bus;
-
245
-
246 pdev->dev.bus = &platform_bus_type;
-
247
-
/*++++++++++++++
-
The platform_device.dev.bus_id is the canonical name for the devices.
-
It's built from two components:
-
* platform_device.name ... which is also used to for driver matching.
-
* platform_device.id ... the device instance number, or else "-1"
-
to indicate there's only one.
-
These are concatenated, so name/id "serial"/0 indicates bus_id "serial.0", and
-
"serial/3" indicates bus_id "serial.3"; both would use the platform_driver
-
named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id)
-
and use the platform_driver called "my_rtc".
-
++++++++++++++*/
-
248 if (pdev->id != -1)
-
249 snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
-
250 pdev->id);
-
251 else
-
252 strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
-
253
-
設定裝置struct device 的bus_id成員,留心這個地方,在以後還需要用到這個的。
-
254 for (i = 0; i < pdev->num_resources; i++) {
-
255 struct resource *p, *r = &pdev->resource[i];
-
256
-
257 if (r->name == NULL)
-
258 r->name = pdev->dev.bus_id;
-
259
-
260 p = r->parent;
-
261 if (!p) {
-
262 if (r->flags & IORESOURCE_MEM)
-
263 p = &iomem_resource;
-
264 else if (r->flags & IORESOURCE_IO)
-
265 p = &ioport_resource;
-
266 }
-
//resources分為兩種IORESOURCE_MEM和IORESOURCE_IO
-
//CPU對外設IO埠實體地址的編址方式有兩種:I/O對映方式和記憶體對映方式
-
267
-
268 if (p && insert_resource(p, r)) {
-
269 printk(KERN_ERR
-
270 "%s: failed to claim resource %d/n",
-
271 pdev->dev.bus_id, i);
-
272 ret = -EBUSY;
-
273 goto failed;
-
274 }
-
275 }
-
276
-
277 pr_debug("Registering platform device '%s'. Parent at %s/n",
-
278 pdev->dev.bus_id, pdev->dev.parent->bus_id);
-
279
-
280 ret = device_add(&pdev->dev);
-
281 if (ret == 0)
-
282 return ret;
-
283
-
284 failed:
-
285 while (--i >= 0)
-
286 if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
-
287 release_resource(&pdev->resource[i]);
-
288 return ret;
-
289}
-
290EXPORT_SYMBOL_GPL(platform_device_add);
由platform_device_register和platform_device_add的實現可知,device_register()和platform_device_register()都會首先初始化裝置
區別在於第二步:其實platform_device_add()包括device_add(),不過要先註冊resources,然後將裝置掛接到特定的platform匯流排。
4 device_driver和platform driver
Platform device是一種device自己是不會做事情的,要有人為它做事情,那就是platform driver。platform driver遵循linux系統的driver model。對於device的discovery/enumerate都不是driver自己完成的而是由由系統的driver註冊機制完成。driver編寫人員只要將註冊必須的資料結構初始化並呼叫註冊driver的kernel API就可以了。
接下來來看platform_driver結構體的原型定義,在http://lxr.linux.no/#linux+v2.6.25/include/linux/platform_device.h#L48中,程式碼如下:
-
48 struct platform_driver {
-
49 int (*probe)(struct platform_device *);
-
50 int (*remove)(struct platform_device *);
-
51 void (*shutdown)(struct platform_device *);
-
52 int (*suspend)(struct platform_device *, pm_message_t state);
-
53 int (*suspend_late)(struct platform_device *, pm_message_t state);
-
54 int (*resume_early)(struct platform_device *);
-
55 int (*resume)(struct platform_device *);
-
56 struct device_driver driver;
-
57};
可見,它包含了裝置操作的幾個功能函式,同時包含了一個device_driver結構,說明device_driver是platform_driver的基類。驅動程式中需要初始化這個變數。下面看一下這個變數的定義,位於http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L121中:
-
121struct device_driver {
-
122 const char *name;
-
123 struct bus_type *bus;
-
124
-
125 struct module *owner;
-
126 const char *mod_name; /* used for built-in modules */
-
127
-
128 int (*probe) (struct device *dev);
-
129 int (*remove) (struct device *dev);
-
130 void (*shutdown) (struct device *dev);
-
131 int (*suspend) (struct device *dev, pm_message_t state);
-
132 int (*resume) (struct device *dev);
-
133 struct attribute_group **groups;
-
134
-
135 struct driver_private *p;
-
136};
device_driver提供了一些操作介面,但其並沒有實現,相當於一些虛擬函式,由派生類platform_driver進行過載,無論何種型別的driver都是基於device_driver派生而來的,具體的各種操作都是基於統一的基類介面的,這樣就實現了面向物件的設計。
需要注意這兩個變數:name和owner。其作用主要是為了和相關的platform_device關聯起來,owner的作用是說明模組的所有者,驅動程式中一般初始化為THIS_MODULE。
device_driver結構中也有一個name變數。platform_driver從字面上來看就知道是裝置驅動。裝置驅動是為誰服務的呢?當然是裝置了。核心正是通過這個一致性來為驅動程式找到資源,即 platform_device中的resource。
5 driver_register 和platform_driver_register
核心提供的platform_driver結構體的註冊函式為platform_driver_register(),其原型定義在http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L458檔案中,具體實現程式碼如下:
-
439/**
-
440 * platform_driver_register
-
441 * @drv: platform driver structure
-
442 */
-
443int platform_driver_register(struct platform_driver *drv)
-
444{
-
445 drv->driver.bus = &platform_bus_type;
-
/*設定成platform_bus_type這個很重要,因為driver和device是通過bus聯絡在一起的,具體在本例中是通過 platform_bus_type中註冊的回撥例程和屬性來是實現的, driver與device的匹配就是通過 platform_bus_type註冊的回撥例程platform_match ()來完成的。*/
-
446 if (drv->probe)
-
447 drv->driver.probe = platform_drv_probe;
-
//在really_probe函式中,回調了platform_drv_probe函式
-
448 if (drv->remove)
-
449 drv->driver.remove = platform_drv_remove;
-
450 if (drv->shutdown)
-
451 drv->driver.shutdown = platform_drv_shutdown;
-
452 if (drv->suspend)
-
453 drv->driver.suspend = platform_drv_suspend;
-
454 if (drv->resume)
-
455 drv->driver.resume = platform_drv_resume;
-
456 return driver_register(&drv->driver);
-
457}
-
458EXPORT_SYMBOL_GPL(platform_driver_register);
不要被上面的platform_drv_XXX嚇倒了,它們其實很簡單,就是將struct device轉換為struct platform_device和struct platform_driver,然後呼叫platform_driver中的相應介面函式。那為什麼不直接呼叫platform_drv_XXX等介面呢?這就是Linux核心中面向物件的設計思想。
device_driver提供了一些操作介面,但其並沒有實現,相當於一些虛擬函式,由派生類platform_driver進行過載,無論何種型別的driver都是基於device_driver派生而來的,device_driver中具體的各種操作都是基於統一的基類介面的,這樣就實現了面向物件的設計。
在檔案http://lxr.linux.no/#linux+v2.6.25/drivers/base/driver.c#L234中,實現了driver_register()函式。
-
209/**
-
210 * driver_register - register driver with bus
-
211 * @drv: driver to register
-
212 *
-
213 * We pass off most of the work to the bus_add_driver() call,
-
214 * since most of the things we have to do deal with the bus
-
215 * structures.
-
216 */
-
217int driver_register(struct device_driver *drv)
-
218{
-
219 int ret;
-
220
-
//如果匯流排的方法和裝置自己的方法同時存在,將列印告警資訊,對於platform bus,其沒有probe等介面
-
221 if ((drv->bus->probe && drv->probe) ||
-
222 (drv->bus->remove && drv->remove) ||
-
223 (drv->bus->shutdown && drv->shutdown))
-
224 printk(KERN_WARNING "Driver '%s' needs updating - please use "
-
225 "bus_type methods/n", drv->name);
-
226 ret = bus_add_driver(drv);
-
227 if (ret)
-
228 return ret;
-
229 ret = driver_add_groups(drv, drv->groups);
-
230 if (ret)
-
231 bus_remove_driver(drv);
-
232 return ret;
-
233}
-
234EXPORT_SYMBOL_GPL(driver_register);
226 其主要將驅動掛接到總線上,通過匯流排來驅動裝置。
-
644/**
-
645 * bus_add_driver - Add a driver to the bus.
-
646 * @drv: driver.
-
647 */
-
648int bus_add_driver(struct device_driver *drv)
-
649{
-
650 struct bus_type *bus;
-
651 struct driver_private *priv;
-
652 int error = 0;
-
653
-
654 bus = bus_get(drv->bus);
-
655 if (!bus)
-
656 return -EINVAL;
-
657
-
658 pr_debug("bus: '%s': add driver %s/n", bus->name, drv->name);
-
659
-
660 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-
661 if (!priv) {
-
662 error = -ENOMEM;
-
663 goto out_put_bus;
-
664 }
-
665 klist_init(&priv->klist_devices, NULL, NULL);
-
666 priv->driver = drv;
-
667 drv->p = priv;
-
668 priv->kobj.kset = bus->p->drivers_kset;
-
669 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
-
670 "%s", drv->name);
-
671 if (error)
-
672 goto out_unregister;
-
673
-
674 if (drv->bus->p->drivers_autoprobe) {
-
675 error = driver_attach(drv);
-
676 if (error)
-
677 goto out_unregister;
-
678 }
-
679 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
-
680 module_add_driver(drv->owner, drv);
-
681
-
682 error = driver_create_file(drv, &driver_attr_uevent);
-
683 if (error) {
-
684 printk(KERN_ERR "%s: uevent attr (%s) failed/n",
-
685 __FUNCTION__, drv->name);
-
686 }
-
687 error = driver_add_attrs(bus, drv);
-
688 if (error) {
-
689 /* How the hell do we get out of this pickle? Give up */
-
690 printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",
-
691 __FUNCTION__, drv->name);
-
692 }
-
693 error = add_bind_files(drv);
-
694 if (error) {
-
695 /* Ditto */
-
696 printk(KERN_ERR "%s: add_bind_files(%s) failed/n",
-
697 __FUNCTION__, drv->name);
-
698 }
-
699
-
700 kobject_uevent(&priv->kobj, KOBJ_ADD);
-
701 return error;
-
702out_unregister:
-
703 kobject_put(&priv->kobj);
-
704out_put_bus:
-
705 bus_put(bus);
-
706 return error;
-
707}
如果總線上的driver是自動probe的話,則將該總線上的driver和device繫結起來。
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L285
-
272/**
-
273 * driver_attach - try to bind driver to devices.
-
274 * @drv: driver.
-
275 *
-
276 * Walk the list of devices that the bus has on it and try to
-
277 * match the driver with each one. If driver_probe_device()
-
278 * returns 0 and the @dev->driver is set, we've found a
-
279 * compatible pair.
-
280 */
-
281int driver_attach(struct device_driver *drv)
-
282{
-
283 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
-
284}
-
285EXPORT_SYMBOL_GPL(driver_attach);
掃描該總線上的每一個裝置,將當前driver和總線上的裝置進行match,如果匹配成功,則將裝置和driver繫結起來。
-
246static int __driver_attach(struct device *dev, void *data)
-
247{
-
248 struct device_driver *drv = data;
-
249
-
250 /*
-
251 * Lock device and try to bind to it. We drop the error
-
252 * here and always return 0, because we need to keep trying
-
253 * to bind to devices and some drivers will return an error
-
254 * simply if it didn't support the device.
-
255 *
-
256 * driver_probe_device() will spit a warning if there
-
257 * is an error.
-
258 */
-
259
-
260 if (dev->parent) /* Needed for USB */
-
261 down(&dev->parent->sem);
-
262 down(&dev->sem);
-
263 if (!dev->driver)
-
264 driver_probe_device(drv, dev);
-
265 up(&dev->sem);
-
266 if (dev->parent)
-
267 up(&dev->parent->sem);
-
268
-
269 return 0;
-
270}
263,如果該裝置尚沒有匹配的driver,則嘗試匹配。
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L187
-
170/**
-
171 * driver_probe_device - attempt to bind device & driver together
-
172 * @drv: driver to bind a device to
-
173 * @dev: device to try to bind to the driver
-
174 *
-
175 * First, we call the bus's match function, if one present, which should
-
176 * compare the device IDs the driver supports with the device IDs of the
-
177 * device. Note we don't do this ourselves because we don't know the
-
178 * format of the ID structures, nor what is to be considered a match and
-
179 * what is not.
-
180 *
-
181 * This function returns 1 if a match is found, -ENODEV if the device is
-
182 * not registered, and 0 otherwise.
-
183 *
-
184 * This function must be called with @dev->sem held. When called for a
-
185 * USB interface, @dev->parent->sem must be held as well.
-
186 */
-
187int driver_probe_device(struct device_driver *drv, struct device *dev)
-
188{
-
189 int ret = 0;
-
190
-
191 if (!device_is_registered(dev))
-
192 return -ENODEV;
-
193 if (drv->bus->match && !drv->bus->match(dev, drv))
-
194 goto done;
-
195
-
196 pr_debug("bus: '%s': %s: matched device %s with driver %s/n",
-
197 drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
-
198
-
199 ret = really_probe(dev, drv);
-
200
-
201done:
-
202 return ret;
-
203}
193,如果該總線上的裝置需要進行匹配,則驗證是否匹配。對於platform匯流排,其匹配過程如下:
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L555
-
542/**
-
543 * platform_match - bind platform device to platform driver.
-
544 * @dev: device.
-
545 * @drv: driver.
-
546 *
-
547 * Platform device IDs are assumed to be encoded like this:
-
548 * "<name><instance>", where <name> is a short description of the type of
-
549 * device, like "pci" or "floppy", and <instance> is the enumerated
-
550 * instance of the device, like '0' or '42'. Driver IDs are simply
-
551 * "<name>". So, extract the <name> from the platform_device structure,
-
552 * and compare it against the name of the driver. Return whether they match
-
553 * or not.
-
554 */
-
555static int platform_match(struct device *dev, struct device_driver *drv)
-
556{
-
557 struct platform_device *pdev;
-
558
-
559 pdev = container_of(dev, struct platform_device, dev);
-
560 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
-
561}
560,簡單的進行字串匹配,這也是我們強調platform_device和platform_driver中的name屬性需要一致的原因。
匹配成功後,則呼叫probe介面。
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L101
-
98static atomic_t probe_count = ATOMIC_INIT(0);
-
99static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
-
100
-
101static int really_probe(struct device *dev, struct device_driver *drv)
-
102{
-
103 int ret = 0;
-
104
-
105 atomic_inc(&probe_count);
-
106 pr_debug("bus: '%s': %s: probing driver %s with device %s/n",
-
107 drv->bus->name, __FUNCTION__, drv->name, dev->bus_id);
-
108 WARN_ON(!list_empty(&dev->devres_head));
-
109
-
110 dev->driver = drv;
-
111 if (driver_sysfs_add(dev)) {
-
112 printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
-
113 __FUNCTION__, dev->bus_id);
-
114 goto probe_failed;
-
115 }
-
116
-
117 if (dev->bus->probe) {
-
118 ret = dev->bus->probe(dev);
-
119 if (ret)
-
120 goto probe_failed;
-
121 } else if (drv->probe) {
-
122 ret = drv->probe(dev);
-
123 if (ret)
-
124 goto probe_failed;
-
125 }
-
126
-
127 driver_bound(dev);
-
128 ret = 1;
-
129 pr_debug("bus: '%s': %s: bound device %s to driver %s/n",
-
130 drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
-
131 goto done;
-
132
-
133probe_failed:
-
134 devres_release_all(dev);
-
135 driver_sysfs_remove(dev);
-
136 dev->driver = NULL;
-
137
-
138 if (ret != -ENODEV && ret != -ENXIO) {
-
139 /* driver matched but the probe failed */
-
140 printk(KERN_WARNING
-
141 "%s: probe of %s failed with error %d/n",
-
142 drv->name, dev->bus_id, ret);
-
143 }
-
144 /*
-
145 * Ignore errors returned by ->probe so that the next driver can try
-
146 * its luck.
-
147 */
-
148 ret = 0;
-
149done:
-
150 atomic_dec(&probe_count);
-
151 wake_up(&probe_waitqueue);
-
152 return ret;
-
153}
-
154
如果bus和driver同時具備probe方法,則優先呼叫匯流排的probe函式。否則呼叫device_driver的probe函式,此probe函式是經過各種型別的driver過載的函式,這就實現了利用基類的統一方法來實現不同的功能。對於platform_driver來說,其就是:
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L394
-
394static int platform_drv_probe(struct device *_dev)
-
395{
-
396 struct platform_driver *drv = to_platform_driver(_dev->driver);
-
397 struct platform_device *dev = to_platform_device(_dev);
-
398
-
399 return drv->probe(dev);
-
400}
然後呼叫特定platform_driver所定義的操作方法,這個是在定義某個platform_driver時靜態指定的操作介面。
至此,platform_driver成功掛接到platform bus上了,並與特定的裝置實現了繫結,並對裝置進行了probe處理。
6 bus、device及driver三者之間的關係
在資料結構設計上,匯流排、裝置及驅動三者相互關聯。
platform device包含device,根據device可以獲得相應的bus及driver。
裝置新增到總線上後形成一個雙向迴圈連結串列,根據匯流排可以獲得其上掛接的所有device,進而獲得了 platform device。根據device也可以獲得驅動該總線上所有裝置的相關driver。
platform driver包含driver,根據driver可以獲得相應的bus,進而獲得bus上所有的device,進一步獲得platform device,根據name對driver與platform device進行匹配,匹配成功後將device與相應的driver關聯起來,即實現了platform device和platform driver的關聯。
匹配成功後呼叫driver的probe進而呼叫platform driver的probe,在probe裡實現驅動特定的功能。
7 哪些適用於plarform驅動?
platform機制將裝置本身的資源註冊進核心,由核心統一管理,在驅動程式中使用這些資源時通過platform device提供的標準介面進行申請並使用。這樣提高了驅動和資源管理的獨立性,這樣擁有更好的可移植性。platform機制的本身使用並不複雜,由兩部分組成:platform_device和platfrom_driver。Platform driver通過platform bus獲取platform_device。
通常情況下只要和核心本身執行依賴性不大的外圍裝置,相對獨立的,擁有各自獨立的資源(地址匯流排和IRQs),都可以用 platform_driver來管理,而timer,irq等小系統之內的裝置則最好不用platfrom_driver機制。
platform_device最大的特定是CPU直接定址裝置的暫存器空間,即使對於其他匯流排裝置,裝置本身的暫存器無法通過CPU匯流排訪問,但匯流排的controller仍然需要通過platform bus來管理。
總之,platfrom_driver的根本目的是為了統一管理系統的外設資源,為驅動程式提供統一的介面來訪問系統資源,將驅動和資源分離,提高程式的可移植性。
8 基於platform匯流排的驅動開發流程
基於Platform匯流排的驅動開發流程如下:
• 定義初始化platform bus
• 定義各種platform devices
• 註冊各種platform devices
• 定義相關platform driver
• 註冊相關platform driver
• 操作相關裝置
圖 platform機制開發驅動流程
以S3C24xx平臺為例,來簡單講述下platform驅動的實現流程。
8.1 初始化platform_bus
Platform匯流排的初始化是在platform_bus_init()完成的,程式碼如下:
-
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L621
-
26struct device platform_bus = {
-
27 .bus_id = "platform",
-
28};
-
29EXPORT_SYMBOL_GPL(platform_bus);
-
621int __init platform_bus_init(void)
-
622{
-
623 int error;
-
624
-
625 error = device_register(&platform_bus);
-
626 if (error)
-
627 return error;
-
628 error = bus_register(&platform_bus_type);
-
629 if (error)
-
630 device_unregister(&platform_bus);
-
631 return error;
-
632}
該函式建立了一個名為 “platform”的裝置,後續platform的裝置都會以此為parent。在sysfs中表示為:所有platform型別的裝置都會新增在 platform_bus所代表的目錄下,即 /sys/devices/platform下面。
-sh-3.1# ls /sys/devices/platform/
Fixed MDIO bus.0 fsl-i2c.0 serial8250
fsl-ehci.0 fsl-i2c.1 serial8250.0
fsl-gianfar.0 mpc83xx_spi.0 uevent
fsl-gianfar.1 mpc83xx_wdt.0
fsl-gianfar_mdio.-5 power
-sh-3.1# ls /sys/
block/ class/ firmware/ kernel/ power/
bus/ devices/ fs/ module/
-sh-3.1# ls /sys/bus/
i2c/ of_platform/ pci_express/ scsi/ usb/
mdio_bus/ pci/ platform/ spi/
-sh-3.1# ls /sys/bus/i2c/
devices/ drivers_autoprobe uevent
drivers/ drivers_probe
-sh-3.1# ls /sys/bus/platform/devices/
Fixed MDIO bus.0/ fsl-gianfar_mdio.-5/ mpc83xx_wdt.0/
fsl-ehci.0/ fsl-i2c.0/ serial8250/
fsl-gianfar.0/ fsl-i2c.1/ serial8250.0/
fsl-gianfar.1/ mpc83xx_spi.0/
-sh-3.1# ls /sys/bus/platform/drivers
drivers/ drivers_autoprobe drivers_probe
-sh-3.1# ls /sys/bus/platform/drivers/
fsl-ehci/ fsl-gianfar_mdio/ mpc83xx_spi/ serial8250/
fsl-gianfar/ fsl-i2c/ mpc83xx_wdt/
platform_bus必須在系統註冊任何platform driver和platform device之前初始化,那麼這是如何實現的呢?
http://lxr.linux.no/#linux+v2.6.25/drivers/base/init.c
-
14/**
-
15 * driver_init - initialize driver model.
-
16 *
-
17 * Call the driver model init functions to initialize their
-
18 * subsystems. Called early from init/main.c.
-
19 */
-
20void __init driver_init(void)
-
21{
-
22 /* These are the core pieces */
-
23 devices_init();
-
24 buses_init();
-
25 classes_init();
-
26 firmware_init();
-
27 hypervisor_init();
-
28
-
29 /* These are also core pieces, but must come after the
-
30 * core core pieces.
-
31 */
-
32 platform_bus_init();
-
33 system_bus_init();
-
34 cpu_dev_init();
-
35 memory_dev_init();
-
36}
init/main.c
start_kernel 》 rest_init 》 kernel_init 》 do_basic_setup》driver_init 》platform_bus_init
http://lxr.linux.no/#linux+v2.6.25/drivers/base/init.c#L32
-
724/*
-
725 * Ok, the machine is now initialized. None of the devices
-
726 * have been touched yet, but the CPU subsystem is up and
-
727 * running, and memory and process management works.
-
728 *
-
729 * Now we can finally start doing some real work..
-
730 */
-
731static void __init do_basic_setup(void)
-
732{
-
733 /* drivers will send hotplug events */
-
734 init_workqueues();
-
735 usermodehelper_init();
-
736 driver_init();
-
737 init_irq_proc();
-
738 do_initcalls();
-
739}
platform driver和platform device的初始化是在do_initcalls中進行的。
8.2 定義platform_device
http://lxr.linux.no/#linux+v2.6.25/arch/arm/plat-s3c24xx/devs.c#L276中定義了系統的資源,是一個高度可移植的檔案,大部分板級資源都在這裡集中定義。
-
274/* I2C */
-
275
-
276static struct resource s3c_i2c_resource[] = {
-
277 [0] = {
-
278 .start = S3C24XX_PA_IIC,
-
279 .end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
-
280 .flags = IORESOURCE_MEM,
-
281 },
-
282 [1] = {
-
283 .start = IRQ_IIC,
-
284 .end = IRQ_IIC,
-
285 .flags = IORESOURCE_IRQ,
-
286 }
-
287
-
288};
-
289
-
290struct platform_device s3c_device_i2c = {
-
291 .name = "s3c2410-i2c",
-
292 .id = -1,
-
293 .num_resources = ARRAY_SIZE(s3c_i2c_resource),
-
294 .resource = s3c_i2c_resource,
-
295};
-
296
-
297EXPORT_SYMBOL(s3c_device_i2c);
裝置名稱為s3c2410-i2c,“-1”只有一個i2c裝置,兩個資源s3c_i2c_resource,分別為i2c控制器的暫存器空間和中斷資訊。
8.3 註冊platform_device
定義了platform_device後,需要新增到系統中,就可以呼叫函式platform_add_devices。
http://lxr.linux.no/#linux+v2.6.25/arch/arm/mach-s3c2440/mach-smdk2440.c
smdk2440_devices將系統資源組織起來,統一註冊進核心。
-
151static struct platform_device *smdk2440_devices[] __initdata = {
-
152 &s3c_device_usb,
-
153 &s3c_device_lcd,
-
154 &s3c_device_wdt,
-
155 &s3c_device_i2c,
-
156 &s3c_device_iis,
-
157};
-
166static void __init smdk2440_machine_init(void)
-
167{
-
168 s3c24xx_fb_set_platdata(&smdk2440_fb_info);
-
169
-
170 platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
-
171 smdk_machine_init();
-
172}
-
173
-
174MACHINE_START(S3C2440, "SMDK2440")
-
175 /* Maintainer: Ben Dooks <[email protected]> */
-
176 .phys_io = S3C2410_PA_UART,
-
177 .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
-
178 .boot_params = S3C2410_SDRAM_PA + 0x100,
-
179
-
180 .init_irq = s3c24xx_init_irq,
-
181 .map_io = smdk2440_map_io,
-
182 .init_machine = smdk2440_machine_init,
-
183 .timer = &s3c24xx_timer,
-
184MACHINE_END
170 platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
將系統所有資源註冊進系統,在此之前platform bus需要初始化成功,否則無法將platform devices掛接到platform bus上。為了保證platform drive初始化時,相關platform資源已經註冊進系統,smdk2440_machine_init需要很早執行,而其作為平臺初始化init_machine 時,將優先於系統所有驅動的初始化。
其呼叫順序如下:
start_kernel》setup_arch》init_machine》arch_initcall(customize_machine)
http://lxr.linux.no/#linux+v2.6.25/arch/arm/kernel/setup.c#L788
-
786arch_initcall(customize_machine);
-
787
-
788void __init setup_arch(char **cmdline_p)
-
789{
-
790 struct tag *tags = (struct tag *)&init_tags;
-
791 struct machine_desc *mdesc;
-
792 char *from = default_command_line;
-
793
-
794 setup_processor();
-
795 mdesc = setup_machine(machine_arch_type);
-
//根據machine id獲得移植時定義的machine desc結構
-
796 machine_name = mdesc->name;
-
797
-
798 if (mdesc->soft_reboot)
-
799 reboot_setup("s");
-
800
-
801 if (__atags_pointer)
-
802 tags = phys_to_virt(__atags_pointer);
-
803 else if (mdesc->boot_params)
-
804 tags = phys_to_virt(mdesc->boot_params);
-
805
-
806 /*
-
807 * If we have the old style parameters, convert them to
-
808 * a tag list.
-
809 */
-
810 if (tags->hdr.tag != ATAG_CORE)
-
811 convert_to_tag_list(tags);
-
812 if (tags->hdr.tag != ATAG_CORE)
-
813 tags = (struct tag *)&init_tags;
-
814
-
815 if (mdesc->fixup)
-
816 mdesc->fixup(mdesc, tags, &from, &meminfo);
-
817
-
818 if (tags->hdr.tag == ATAG_CORE) {
-
819 if (meminfo.nr_banks != 0)
-
820 squash_mem_tags(tags);
-
821 save_atags(tags);
-
822 parse_tags(tags);
-
823 }
-
824
-
825 init_mm.start_code = (unsigned long) &_text;
-
826 init_mm.end_code = (unsigned long) &_etext;
-
827 init_mm.end_data = (unsigned long) &_edata;
-
828 init_mm.brk = (unsigned long) &_end;
-
829
-
830 memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
-
831 boot_command_line[COMMAND_LINE_SIZE-1] = '/0';
-
832 parse_cmdline(cmdline_p, from);
-
833 paging_init(&meminfo, mdesc);
-
834 request_standard_resources(&meminfo, mdesc);
-
835
-
836#ifdef CONFIG_SMP
-
837 smp_init_cpus();
-
838#endif
-
839
-
840 cpu_init();
-
841
-
842 /*
-
843 * Set up various architecture-specific pointers
-
844 */
-
845 init_arch_irq = mdesc->init_irq;
-
846 system_timer = mdesc->timer;
-
847 init_machine = mdesc->init_machine;
-
//對init_machine指標賦值
-
848
-
849#ifdef CONFIG_VT
-
850#if defined(CONFIG_VGA_CONSOLE)
-
851 conswitchp = &vga_con;
-
852#elif defined(CONFIG_DUMMY_CONSOLE)
-
853 conswitchp = &dummy_con;
-
854#endif
-
855#endif
-
856}
-
777static void (*init_machine)(void) __initdata;
-
778
-
779static int __init customize_machine(void)
-
780{
-
781 /* customizes platform devices, or adds new ones */
-
782 if (init_machine)
-
783 init_machine();
-
784 return 0;
-
785}
-
786arch_initcall(customize_machine);
arch_initcall將customize_machine放在特定的段中,系統將在某個地方執行所有的arch_initcall修飾的函式。
http://lxr.linux.no/#linux+v2.6.25/include/linux/init.h#L182
-
152#ifndef MODULE //非可載入模組,即編譯連結進核心的程式碼
-
153
-
154#ifndef __ASSEMBLY__
-
155
-
156/* initcalls are now grouped by functionality into separate
-
157 * subsections. Ordering inside the subsections is determined
-
158 * by link order.
-
159 * For backwards compatibility, initcall() puts the call in
-
160 * the device init subsection.
-
161 *
-
162 * The `id' arg to __define_initcall() is needed so that multiple initcalls
-
163 * can point at the same handler without causing duplicate-symbol build errors.
-
164 */
-
165
-
166#define __define_initcall(level,fn,id) /
-
167 static initcall_t __initcall_##fn##id __used /
-
168 __attribute__((__section__(".initcall" level ".init"))) = fn
-
169
-
170/*
-
171 * A "pure" initcall has no dependencies on anything else, and purely
-
172 * initializes variables that couldn't be statically initialized.
-
173 *
-
174 * This only exists for built-in code, not for modules.
-
175 */
-
176#define pure_initcall(fn) __define_initcall("0",fn,0)
-
177
-
178#define core_initcall(fn) __define_initcall("1",fn,1)
-
179#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
-
180#define postcore_initcall(fn) __define_initcall("2",fn,2)
-
181#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
-
182#define arch_initcall(fn) __define_initcall("3",fn,3)
-
183#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
-
184#define subsys_initcall(fn) __define_initcall("4",fn,4)
-
185#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
-
186#define fs_initcall(fn) __define_initcall("5",fn,5)
-
187#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
-
188#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
-
189#define device_initcall(fn) __define_initcall("6",fn,6)
-
190#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
-
191#define late_initcall(fn) __define_initcall("7",fn,7)
-
192#define late_initcall_sy